From 874267b79dacb9a4271b1895dc76ec979a1e1a63 Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Wed, 29 Nov 2017 16:44:57 +0300 Subject: [PATCH] Report cyclic scopes properly This commit introduces proper handling of recursion in scopes, which could occur when some of companion object supertypes are members of that companion owner: ``` class Container { open class Base companion object : Base() } ``` To resolve `Base`, we have to build member scope for `Container`. In the member scope of `Container`, we see all classifiers from companion and his supertypes So, we have to resolve companion objects supertype, which happens to be `Base` again - therefore, we encounter recursion here. Previously, we created `ThrowingLexicalScope` for such recursive calls, but didn't checked for loop explicitly, which lead to a wide variety of bugs (see https://jetbrains.quip.com/dc5aABhZoaQY and KT-10532). To report such cyclic declarations properly, we first change `ThrowingLexicalScope` to `ErrorLexicalScope` -- the main difference is that latter doesn't throws ISE when someone tries to resolve type in it, allowing us to report error instead of crashing with exception. Then, we add additional fake edge in supertypes graph (from host-class to companion object) which allows us to piggyback on existing supertypes loops detection mechanism, and report such cycles for user. --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 2 + .../kotlin/resolve/findLoopsInSupertypes.kt | 7 + .../ClassResolutionScopesSupport.kt | 16 +- .../lazy/descriptors/LazyClassDescriptor.java | 13 ++ .../kotlin/resolve/scopes/utils/ScopeUtils.kt | 49 +++--- .../withCompanion/everythingInOneScope.kt | 14 ++ .../withCompanion/everythingInOneScope.txt | 31 ++++ .../withCompanion/noMembers.kt | 14 ++ .../withCompanion/noMembers.txt | 43 ++++++ .../withCompanion/onlyInterfaces.kt | 17 +++ .../withCompanion/onlyInterfaces.txt | 29 ++++ .../withCompanion/typeIsLowEnough.kt | 20 +++ .../withCompanion/typeIsLowEnough.txt | 40 +++++ .../withCompanion/withIrrelevantInterface.kt | 19 +++ .../withCompanion/withIrrelevantInterface.txt | 43 ++++++ .../withCompanion/withMembers.kt | 63 ++++++++ .../withCompanion/withMembers.txt | 139 ++++++++++++++++++ .../withCompanion/withoutTypeReference.kt | 17 +++ .../withCompanion/withoutTypeReference.txt | 40 +++++ .../tests/inner/classesInClassObjectHeader.kt | 6 +- .../tests/inner/innerErrorForClassObjects.kt | 4 +- .../checkers/DiagnosticsTestGenerated.java | 51 +++++++ .../DiagnosticsUsingJavacTestGenerated.java | 51 +++++++ .../types/AbstractClassTypeConstructor.java | 33 ++++- .../kotlin/types/AbstractTypeConstructor.kt | 24 ++- 26 files changed, 741 insertions(+), 45 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.kt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.txt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.kt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.txt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.kt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.txt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.kt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.txt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.kt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.txt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.kt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.txt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.kt create mode 100644 compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index dcb29dd6cf9..d8972b26709 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -262,6 +262,7 @@ public interface Errors { DiagnosticFactory0.create(ERROR, VARIANCE_IN_PROJECTION); DiagnosticFactory0 CYCLIC_INHERITANCE_HIERARCHY = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 CYCLIC_SCOPES_WITH_COMPANION = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 SUPERTYPE_NOT_INITIALIZED = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index b0fc0e13a5a..66d39a66786 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -538,6 +538,8 @@ public class DefaultErrorMessages { MAP.put(TYPE_MISMATCH_IN_RANGE, "Type mismatch: incompatible types of range and element checked in it"); MAP.put(CYCLIC_INHERITANCE_HIERARCHY, "There's a cycle in the inheritance hierarchy for this type"); MAP.put(CYCLIC_GENERIC_UPPER_BOUND, "Type parameter has cyclic upper bounds"); + MAP.put(CYCLIC_SCOPES_WITH_COMPANION, "There's a cycle in scopes for that type. Most probably, there's a companion object that inherits some nested class (see KT-21515).\n" + + "Such code is currently unstable, and its behavior may change in future releases"); MAP.put(MANY_CLASSES_IN_SUPERTYPE_LIST, "Only one class may appear in a supertype list"); MAP.put(SUPERTYPE_NOT_A_CLASS_OR_INTERFACE, "Only classes and interfaces may serve as supertypes"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/findLoopsInSupertypes.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/findLoopsInSupertypes.kt index 419ddbe7851..cf9aeacbe22 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/findLoopsInSupertypes.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/findLoopsInSupertypes.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.descriptors.SupertypeLoopChecker +import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.utils.DFS @@ -38,6 +39,12 @@ class SupertypeLoopCheckerImpl : SupertypeLoopChecker { if (isReachable(superType.constructor, currentTypeConstructor, graph)) { superTypesToRemove.add(superType) reportLoop(superType) + + currentTypeConstructor.declarationDescriptor?.let { + if (it.isCompanionObject()) { + reportLoop(it.defaultType) + } + } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/ClassResolutionScopesSupport.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/ClassResolutionScopesSupport.kt index fcaee8eee89..8bcd4bb1cdc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/ClassResolutionScopesSupport.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/ClassResolutionScopesSupport.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny import org.jetbrains.kotlin.resolve.scopes.* -import org.jetbrains.kotlin.resolve.scopes.utils.ThrowingLexicalScope +import org.jetbrains.kotlin.resolve.scopes.utils.ErrorLexicalScope import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* @@ -46,26 +46,26 @@ class ClassResolutionScopesSupport( scopeWithGenerics(inheritanceScopeWithMe()) } - private val inheritanceScopeWithoutMe: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createThrowingLexicalScope) { + private val inheritanceScopeWithoutMe: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createErrorLexicalScope) { classDescriptor.getAllSuperclassesWithoutAny().asReversed().fold(getOuterScope()) { scope, currentClass -> createInheritanceScope(parent = scope, ownerDescriptor = classDescriptor, classDescriptor = currentClass) } } - private val inheritanceScopeWithMe: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createThrowingLexicalScope) { + private val inheritanceScopeWithMe: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createErrorLexicalScope) { createInheritanceScope(parent = inheritanceScopeWithoutMe(), ownerDescriptor = classDescriptor, classDescriptor = classDescriptor) } - val scopeForCompanionObjectHeaderResolution: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createThrowingLexicalScope) { + val scopeForCompanionObjectHeaderResolution: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createErrorLexicalScope) { createInheritanceScope(inheritanceScopeWithoutMe(), classDescriptor, classDescriptor, withCompanionObject = false) } - val scopeForMemberDeclarationResolution: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createThrowingLexicalScope) { + val scopeForMemberDeclarationResolution: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createErrorLexicalScope) { val scopeWithGenerics = scopeWithGenerics(inheritanceScopeWithMe()) LexicalScopeImpl(scopeWithGenerics, classDescriptor, true, classDescriptor.thisAsReceiverParameter, LexicalScopeKind.CLASS_MEMBER_SCOPE) } - val scopeForStaticMemberDeclarationResolution: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createThrowingLexicalScope) { + val scopeForStaticMemberDeclarationResolution: () -> LexicalScope = storageManager.createLazyValue(onRecursion = createErrorLexicalScope) { if (classDescriptor.kind.isSingleton) { scopeForMemberDeclarationResolution() } @@ -116,9 +116,7 @@ class ClassResolutionScopesSupport( createLazyValueWithPostCompute(compute, onRecursion, {}) companion object { - private val createThrowingLexicalScope: (Boolean) -> LexicalScope = { - ThrowingLexicalScope() - } + private val createErrorLexicalScope: (Boolean) -> LexicalScope = { ErrorLexicalScope() } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java index 5787eace720..bb0b13cf786 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java @@ -607,6 +607,19 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes } } + @Override + protected void reportScopesLoopError(@NotNull KotlinType type) { + PsiElement reportOn = DescriptorToSourceUtils.getSourceFromDescriptor(type.getConstructor().getDeclarationDescriptor()); + + if (reportOn instanceof KtClass) { + reportOn = ((KtClass) reportOn).getNameIdentifier(); + } + + if (reportOn != null) { + c.getTrace().report(CYCLIC_SCOPES_WITH_COMPANION.on(reportOn)); + } + } + private void reportCyclicInheritanceHierarchyError( @NotNull BindingTrace trace, @NotNull ClassDescriptor classDescriptor, diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt index 7eb021a35ff..bd78a00c28f 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.scopes.* +import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.util.collectionUtils.concat import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.SmartList @@ -245,31 +246,37 @@ fun chainImportingScopes(scopes: List, tail: ImportingScope? = n } } -class ThrowingLexicalScope : LexicalScope { - override val parent: HierarchicalScope - get() = throw IllegalStateException() +class ErrorLexicalScope : LexicalScope { + override val parent: HierarchicalScope = object : HierarchicalScope { + override val parent: HierarchicalScope? = null - override val ownerDescriptor: DeclarationDescriptor - get() = throw IllegalStateException() - override val isOwnerDescriptorAccessibleByLabel: Boolean - get() = throw IllegalStateException() - override val implicitReceiver: ReceiverParameterDescriptor? - get() = throw IllegalStateException() - override val kind: LexicalScopeKind - get() = LexicalScopeKind.THROWING + override fun printStructure(p: Printer) { + p.print("") + } - override fun printStructure(p: Printer) = - throw IllegalStateException() + override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = null - override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = - throw IllegalStateException() + override fun getContributedVariables(name: Name, location: LookupLocation): Collection = emptySet() - override fun getContributedVariables(name: Name, location: LookupLocation): Collection = - throw IllegalStateException() + override fun getContributedFunctions(name: Name, location: LookupLocation): Collection = emptySet() - override fun getContributedFunctions(name: Name, location: LookupLocation): Collection = - throw IllegalStateException() + override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection = emptySet() + } - override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection = - throw IllegalStateException() + override fun printStructure(p: Printer) { + p.print("") + } + + override val ownerDescriptor: DeclarationDescriptor = ErrorUtils.createErrorClass("") + override val isOwnerDescriptorAccessibleByLabel: Boolean = false + override val implicitReceiver: ReceiverParameterDescriptor? = null + override val kind: LexicalScopeKind = LexicalScopeKind.THROWING + + override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = null + + override fun getContributedVariables(name: Name, location: LookupLocation): Collection = emptySet() + + override fun getContributedFunctions(name: Name, location: LookupLocation): Collection = emptySet() + + override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection = emptySet() } diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.kt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.kt new file mode 100644 index 00000000000..84b65570cda --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.kt @@ -0,0 +1,14 @@ +// see https://youtrack.jetbrains.com/issue/KT-21515 + +open class Container { + open class Base { + open fun m() {} + } + + // note that Base() supertype will be resolved in scope that was created on recursion + abstract class DerivedAbstract : Base() + + companion object : DerivedAbstract() { + override fun m() {} + } +} diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.txt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.txt new file mode 100644 index 00000000000..cf0c77ae1f6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.txt @@ -0,0 +1,31 @@ +package + +public open class Container { + public constructor Container() + 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 open class Base { + public constructor Base() + 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 fun m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public companion object Companion : Container.DerivedAbstract { + 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 fun m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public abstract class DerivedAbstract { + public constructor DerivedAbstract() + 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 + } +} diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.kt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.kt new file mode 100644 index 00000000000..638d0e388e3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.kt @@ -0,0 +1,14 @@ +// see https://youtrack.jetbrains.com/issue/KT-21515 + +abstract class DerivedAbstract : C.Base() { + open class Data +} + +public class C { + + open class Base () + + class Foo : Data() + + companion object : DerivedAbstract() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.txt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.txt new file mode 100644 index 00000000000..f7d7d5a2636 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.txt @@ -0,0 +1,43 @@ +package + +public final class C { + public constructor C() + 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 open class Base { + public constructor Base() + 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 companion object Companion : DerivedAbstract { + 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 class Foo : DerivedAbstract.Data { + public constructor Foo() + 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 abstract class DerivedAbstract : C.Base { + public constructor DerivedAbstract() + 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 open class Data { + public constructor Data() + 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 + } +} diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.kt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.kt new file mode 100644 index 00000000000..e2e3643a16c --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.kt @@ -0,0 +1,17 @@ +// see https://youtrack.jetbrains.com/issue/KT-21515 + +open class Container { + // Note that here we also have errors and diagnostics, even though there are actually no loops. + // (this is case because we can't know if there are any loops without resolving, but resolving + // itself provokes loops) + + interface Base { + open fun m() {} + } + + interface DerivedAbstract : Base + + companion object : DerivedAbstract { + override fun m() {} + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.txt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.txt new file mode 100644 index 00000000000..5ed1a8282b3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.txt @@ -0,0 +1,29 @@ +package + +public open class Container { + public constructor Container() + 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 interface Base { + 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 fun m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public companion object Companion : Container.DerivedAbstract { + 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 fun m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface DerivedAbstract { + 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 + } +} diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.kt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.kt new file mode 100644 index 00000000000..39572b1c951 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.kt @@ -0,0 +1,20 @@ +// see https://youtrack.jetbrains.com/issue/KT-21515 + +abstract class DerivedAbstract : C.Base() { + override abstract fun m() +} + +public class C { + class Data + + open class Base () { + open fun m() {} + } + + // Note that Data is resolved succesfully here because we don't step on error-scope + val data: Data = Data() + + companion object : DerivedAbstract() { + override fun m() {} + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.txt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.txt new file mode 100644 index 00000000000..40a85227d41 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.txt @@ -0,0 +1,40 @@ +package + +public final class C { + public constructor C() + public final val data: C.Data + 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 open class Base { + public constructor Base() + 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 fun m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public companion object Companion : DerivedAbstract { + 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*/ fun m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class Data { + public constructor Data() + 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 abstract class DerivedAbstract : C.Base { + public constructor DerivedAbstract() + 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 abstract override /*1*/ fun m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.kt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.kt new file mode 100644 index 00000000000..4b5a114b8e3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.kt @@ -0,0 +1,19 @@ +// see https://youtrack.jetbrains.com/issue/KT-21515 + +interface SomeIrrelevantInterface + +// note that C.Base() supertype will be resolved in normal scope +abstract class DerivedAbstract : C.Base() + +class Data + +public class C { + + val data: Data = Data() + + // Note that any supertype of Base will be resolved in error-scope, even if it absolutely irrelevant + // to the types in cycle. + open class Base() : SomeIrrelevantInterface + + companion object : DerivedAbstract() +} diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.txt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.txt new file mode 100644 index 00000000000..6152bd8f01f --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.txt @@ -0,0 +1,43 @@ +package + +public final class C { + public constructor C() + public final val data: Data + 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 open class Base { + public constructor Base() + 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 companion object Companion : DerivedAbstract { + 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 class Data { + public constructor Data() + 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 abstract class DerivedAbstract : C.Base { + public constructor DerivedAbstract() + 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 interface SomeIrrelevantInterface { + 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 +} diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.kt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.kt new file mode 100644 index 00000000000..3922425564d --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.kt @@ -0,0 +1,63 @@ +// see https://youtrack.jetbrains.com/issue/KT-21515 + +object WithFunctionInBase { + abstract class DerivedAbstract : C.Base() + + class Data + + public class C { + // error-scope + val data: Data = Data() + + open class Base() { + // error-scope + fun foo(): Int = 42 + } + + companion object : DerivedAbstract() + } +} + +object WithPropertyInBase { + // This case is very similar to previous one, but there are subtle differences from POV of implementation + + abstract class DerivedAbstract : C.Base() + + class Data + + public class C { + + open class Base() { + // error-scope + val foo: Int = 42 + } + + // error-scope + val data: Data = Data() + + companion object : DerivedAbstract() + } +} + +object WithPropertyInBaseDifferentOrder { + // This case is very similar to previous one, but there are subtle differences from POV of implementation + // Note how position of property in file affected order of resolve, and, consequently, its results and + // diagnostics. + + abstract class DerivedAbstract : C.Base() + + class Data + + public class C { + // Now it is succesfully resolved (vs. ErrorType like in the previous case) + val data: Data = Data() + + open class Base() { + // Now it is unresolved (vs. ErrorType like in the previous case) + val foo: Int = 42 + + } + + companion object : DerivedAbstract() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.txt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.txt new file mode 100644 index 00000000000..5be70fc4d9c --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.txt @@ -0,0 +1,139 @@ +package + +public object WithFunctionInBase { + private constructor WithFunctionInBase() + 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 class C { + public constructor C() + public final val data: [ERROR : ] + 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 open class Base { + public constructor Base() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): [ERROR : ] + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public companion object Companion : WithFunctionInBase.DerivedAbstract { + private constructor Companion() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun foo(): [ERROR : ] + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + } + + public final class Data { + public constructor Data() + 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 abstract class DerivedAbstract : WithFunctionInBase.C.Base { + public constructor DerivedAbstract() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun foo(): [ERROR : ] + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public object WithPropertyInBase { + private constructor WithPropertyInBase() + 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 class C { + public constructor C() + public final val data: [ERROR : ] + 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 open class Base { + public constructor Base() + public final val foo: [ERROR : ] + 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 companion object Companion : WithPropertyInBase.DerivedAbstract { + private constructor Companion() + public final override /*1*/ /*fake_override*/ val foo: [ERROR : ] + 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 class Data { + public constructor Data() + 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 abstract class DerivedAbstract : WithPropertyInBase.C.Base { + public constructor DerivedAbstract() + public final override /*1*/ /*fake_override*/ val foo: [ERROR : ] + 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 object WithPropertyInBaseDifferentOrder { + private constructor WithPropertyInBaseDifferentOrder() + 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 class C { + public constructor C() + public final val data: WithPropertyInBaseDifferentOrder.Data + 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 open class Base { + public constructor Base() + public final val foo: [ERROR : Int] + 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 companion object Companion : WithPropertyInBaseDifferentOrder.DerivedAbstract { + private constructor Companion() + public final override /*1*/ /*fake_override*/ val foo: [ERROR : Int] + 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 class Data { + public constructor Data() + 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 abstract class DerivedAbstract : WithPropertyInBaseDifferentOrder.C.Base { + public constructor DerivedAbstract() + public final override /*1*/ /*fake_override*/ val foo: [ERROR : Int] + 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 + } +} diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.kt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.kt new file mode 100644 index 00000000000..97dd7b8dec0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.kt @@ -0,0 +1,17 @@ +// see https://youtrack.jetbrains.com/issue/KT-21515 + +abstract class DerivedAbstract : C.Base() + +class Data + +open class C { + open class Base { + open fun m() {} + } + + val field = Data() + + companion object : DerivedAbstract() { + override fun m() {} + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.txt b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.txt new file mode 100644 index 00000000000..0cc501588da --- /dev/null +++ b/compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.txt @@ -0,0 +1,40 @@ +package + +public open class C { + public constructor C() + public final val field: Data + 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 open class Base { + public constructor Base() + 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 fun m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public companion object Companion : DerivedAbstract { + 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*/ fun m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public final class Data { + public constructor Data() + 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 abstract class DerivedAbstract : C.Base { + public constructor DerivedAbstract() + 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 m(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/inner/classesInClassObjectHeader.kt b/compiler/testData/diagnostics/tests/inner/classesInClassObjectHeader.kt index 2c7492d2812..1702c497333 100644 --- a/compiler/testData/diagnostics/tests/inner/classesInClassObjectHeader.kt +++ b/compiler/testData/diagnostics/tests/inner/classesInClassObjectHeader.kt @@ -1,11 +1,11 @@ class Test { @`InnerAnnotation` @InnerAnnotation - companion object : StaticClass(), InnerClass() { + companion object : StaticClass(), InnerClass() { } annotation class InnerAnnotation - open class StaticClass + open class StaticClass - open inner class InnerClass + open inner class InnerClass } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inner/innerErrorForClassObjects.kt b/compiler/testData/diagnostics/tests/inner/innerErrorForClassObjects.kt index cf5fb902ea0..42df6333fee 100644 --- a/compiler/testData/diagnostics/tests/inner/innerErrorForClassObjects.kt +++ b/compiler/testData/diagnostics/tests/inner/innerErrorForClassObjects.kt @@ -5,7 +5,7 @@ class TestSome

{ } class Test { - companion object : InnerClass() { + companion object : InnerClass() { val a = object: InnerClass() { } @@ -22,5 +22,5 @@ class Test { val inClass = 12 fun foo() {} - open inner class InnerClass + open inner class InnerClass } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 0c706440ecd..10f9f25e199 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -4854,6 +4854,57 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/twoClassesWithNestedCycle.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class WithCompanion extends AbstractDiagnosticsTest { + public void testAllFilesPresentInWithCompanion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("everythingInOneScope.kt") + public void testEverythingInOneScope() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.kt"); + doTest(fileName); + } + + @TestMetadata("noMembers.kt") + public void testNoMembers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.kt"); + doTest(fileName); + } + + @TestMetadata("onlyInterfaces.kt") + public void testOnlyInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.kt"); + doTest(fileName); + } + + @TestMetadata("typeIsLowEnough.kt") + public void testTypeIsLowEnough() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.kt"); + doTest(fileName); + } + + @TestMetadata("withIrrelevantInterface.kt") + public void testWithIrrelevantInterface() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.kt"); + doTest(fileName); + } + + @TestMetadata("withMembers.kt") + public void testWithMembers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.kt"); + doTest(fileName); + } + + @TestMetadata("withoutTypeReference.kt") + public void testWithoutTypeReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/dataClasses") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index a42862f317b..0df2fd13e20 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -4854,6 +4854,57 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/twoClassesWithNestedCycle.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class WithCompanion extends AbstractDiagnosticsUsingJavacTest { + public void testAllFilesPresentInWithCompanion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("everythingInOneScope.kt") + public void testEverythingInOneScope() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/everythingInOneScope.kt"); + doTest(fileName); + } + + @TestMetadata("noMembers.kt") + public void testNoMembers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/noMembers.kt"); + doTest(fileName); + } + + @TestMetadata("onlyInterfaces.kt") + public void testOnlyInterfaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/onlyInterfaces.kt"); + doTest(fileName); + } + + @TestMetadata("typeIsLowEnough.kt") + public void testTypeIsLowEnough() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/typeIsLowEnough.kt"); + doTest(fileName); + } + + @TestMetadata("withIrrelevantInterface.kt") + public void testWithIrrelevantInterface() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withIrrelevantInterface.kt"); + doTest(fileName); + } + + @TestMetadata("withMembers.kt") + public void testWithMembers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withMembers.kt"); + doTest(fileName); + } + + @TestMetadata("withoutTypeReference.kt") + public void testWithoutTypeReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cyclicHierarchy/withCompanion/withoutTypeReference.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/dataClasses") diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/AbstractClassTypeConstructor.java b/core/descriptors/src/org/jetbrains/kotlin/types/AbstractClassTypeConstructor.java index 1935bfd3100..4d675d805e3 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/AbstractClassTypeConstructor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/AbstractClassTypeConstructor.java @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.storage.StorageManager; +import org.jetbrains.kotlin.utils.SmartList; import java.util.Collection; import java.util.Collections; @@ -124,17 +125,39 @@ public abstract class AbstractClassTypeConstructor extends AbstractTypeConstruct @NotNull @Override - protected Collection getAdditionalNeighboursInSupertypeGraph() { + protected Collection getAdditionalNeighboursInSupertypeGraph(boolean useCompanions) { + DeclarationDescriptor containingDeclaration = getDeclarationDescriptor().getContainingDeclaration(); + + if (!(containingDeclaration instanceof ClassDescriptor)) { + return Collections.emptyList(); + } + + Collection additionalNeighbours = new SmartList(); + // We suppose that there is an edge from C to A in graph when disconnecting loops in supertypes, // because such cyclic declarations should be prohibited (see p.10.2.1 of Kotlin spec) // class A : B { // static class C {} // } // class B : A.C {} - DeclarationDescriptor containingDeclaration = getDeclarationDescriptor().getContainingDeclaration(); - if (containingDeclaration instanceof ClassDescriptor) { - return Collections.singleton(((ClassDescriptor) containingDeclaration).getDefaultType()); + ClassDescriptor containingClassDescriptor = (ClassDescriptor) containingDeclaration; + additionalNeighbours.add(containingClassDescriptor.getDefaultType()); + + // Also we add edge from host-class to companion object. Together with previous edges + // (from nesteds to containing class), they can create visibility loops like in the + // following example: + // + // class ContainingClass { + // open class Nested {} // to create scope for resolving Nested, we have to resolve CO header + // companion object : Nested() {} // to resolve CO header, we have to resolve Nested + // } + // + // Relates to KT-21515 + ClassDescriptor companion = containingClassDescriptor.getCompanionObjectDescriptor(); + if (useCompanions && companion != null) { + additionalNeighbours.add(companion.getDefaultType()); } - return Collections.emptyList(); + + return additionalNeighbours; } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/AbstractTypeConstructor.kt b/core/descriptors/src/org/jetbrains/kotlin/types/AbstractTypeConstructor.kt index 62e37b49c00..9cf888532d7 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/AbstractTypeConstructor.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/AbstractTypeConstructor.kt @@ -41,27 +41,41 @@ abstract class AbstractTypeConstructor(storageManager: StorageManager) : TypeCon var resultWithoutCycles = supertypeLoopChecker.findLoopsInSupertypesAndDisconnect( this, supertypes.allSupertypes, - { it.computeNeighbours() }, - { reportSupertypeLoopError(it) }) + { it.computeNeighbours(useCompanions = false) }, + { reportSupertypeLoopError(it) } + ) if (resultWithoutCycles.isEmpty()) { resultWithoutCycles = defaultSupertypeIfEmpty()?.let { listOf(it) }.orEmpty() } + // We also check if there are a loop with additional edges going from owner of companion to + // the companion itself. + // Note that we use already disconnected types to not report two diagnostics on cyclic supertypes + supertypeLoopChecker.findLoopsInSupertypesAndDisconnect( + this, resultWithoutCycles, + { it.computeNeighbours(useCompanions = true) }, + { reportScopesLoopError(it) } + ) + supertypes.supertypesWithoutCycles = (resultWithoutCycles as? List) ?: resultWithoutCycles.toList() }) - private fun TypeConstructor.computeNeighbours(): Collection = + private fun TypeConstructor.computeNeighbours(useCompanions: Boolean): Collection = (this as? AbstractTypeConstructor)?.let { abstractClassifierDescriptor -> abstractClassifierDescriptor.supertypes().allSupertypes + - abstractClassifierDescriptor.getAdditionalNeighboursInSupertypeGraph() + abstractClassifierDescriptor.getAdditionalNeighboursInSupertypeGraph(useCompanions) } ?: supertypes protected abstract fun computeSupertypes(): Collection protected abstract val supertypeLoopChecker: SupertypeLoopChecker protected open fun reportSupertypeLoopError(type: KotlinType) {} - protected open fun getAdditionalNeighboursInSupertypeGraph(): Collection = emptyList() + + // TODO: overload in AbstractTypeParameterDescriptor? + protected open fun reportScopesLoopError(type: KotlinType) {} + + protected open fun getAdditionalNeighboursInSupertypeGraph(useCompanions: Boolean): Collection = emptyList() protected open fun defaultSupertypeIfEmpty(): KotlinType? = null }