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.
This commit is contained in:
Dmitry Savvinov
2017-11-29 16:44:57 +03:00
parent 33f9576dd1
commit 874267b79d
26 changed files with 741 additions and 45 deletions
@@ -262,6 +262,7 @@ public interface Errors {
DiagnosticFactory0.create(ERROR, VARIANCE_IN_PROJECTION);
DiagnosticFactory0<PsiElement> CYCLIC_INHERITANCE_HIERARCHY = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> CYCLIC_SCOPES_WITH_COMPANION = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtSuperTypeEntry> SUPERTYPE_NOT_INITIALIZED = DiagnosticFactory0.create(ERROR);
@@ -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");
@@ -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)
}
}
}
}
@@ -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() }
}
}
@@ -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,
@@ -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<ImportingScope>, 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("<FAKE PARENT FOR ERROR LEXICAL SCOPE>")
}
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<VariableDescriptor> = emptySet()
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<VariableDescriptor> =
throw IllegalStateException()
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<FunctionDescriptor> = emptySet()
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<FunctionDescriptor> =
throw IllegalStateException()
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> = emptySet()
}
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> =
throw IllegalStateException()
override fun printStructure(p: Printer) {
p.print("<ERROR_SCOPE>")
}
override val ownerDescriptor: DeclarationDescriptor = ErrorUtils.createErrorClass("<ERROR CLASS FOR ERROR SCOPE>")
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<VariableDescriptor> = emptySet()
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<FunctionDescriptor> = emptySet()
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> = emptySet()
}
@@ -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 <!CYCLIC_SCOPES_WITH_COMPANION!>DerivedAbstract<!> : <!UNRESOLVED_REFERENCE!>Base<!>()
companion <!CYCLIC_SCOPES_WITH_COMPANION!>object<!> : DerivedAbstract() {
<!NOTHING_TO_OVERRIDE!>override<!> fun m() {}
}
}
@@ -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
}
}
@@ -0,0 +1,14 @@
// see https://youtrack.jetbrains.com/issue/KT-21515
abstract class <!CYCLIC_SCOPES_WITH_COMPANION!>DerivedAbstract<!> : C.Base() {
open class Data
}
public class C {
open class <!CYCLIC_SCOPES_WITH_COMPANION!>Base<!> ()
class Foo : Data()
companion <!CYCLIC_SCOPES_WITH_COMPANION!>object<!> : DerivedAbstract()
}
@@ -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
}
}
@@ -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 <!CYCLIC_SCOPES_WITH_COMPANION!>DerivedAbstract<!> : <!UNRESOLVED_REFERENCE!>Base<!>
companion <!CYCLIC_SCOPES_WITH_COMPANION!>object<!> : DerivedAbstract {
<!NOTHING_TO_OVERRIDE!>override<!> fun m() {}
}
}
@@ -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
}
}
@@ -0,0 +1,20 @@
// see https://youtrack.jetbrains.com/issue/KT-21515
abstract class <!CYCLIC_SCOPES_WITH_COMPANION!>DerivedAbstract<!> : C.Base() {
override abstract fun m()
}
public class C {
class Data
open class <!CYCLIC_SCOPES_WITH_COMPANION!>Base<!> () {
open fun m() {}
}
// Note that Data is resolved succesfully here because we don't step on error-scope
val data: Data = Data()
companion <!CYCLIC_SCOPES_WITH_COMPANION!>object<!> : DerivedAbstract() {
override fun m() {}
}
}
@@ -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
}
@@ -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 <!CYCLIC_SCOPES_WITH_COMPANION!>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 <!CYCLIC_SCOPES_WITH_COMPANION!>Base<!>() : <!UNRESOLVED_REFERENCE!>SomeIrrelevantInterface<!>
companion <!CYCLIC_SCOPES_WITH_COMPANION!>object<!> : DerivedAbstract()
}
@@ -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
}
@@ -0,0 +1,63 @@
// see https://youtrack.jetbrains.com/issue/KT-21515
object WithFunctionInBase {
abstract class <!CYCLIC_SCOPES_WITH_COMPANION!>DerivedAbstract<!> : C.Base()
class Data
public class C {
// error-scope
val data: <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>Data<!> = Data()
open class <!CYCLIC_SCOPES_WITH_COMPANION!>Base<!>() {
// error-scope
fun foo(): <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>Int<!> = 42
}
companion <!CYCLIC_SCOPES_WITH_COMPANION!>object<!> : DerivedAbstract()
}
}
object WithPropertyInBase {
// This case is very similar to previous one, but there are subtle differences from POV of implementation
abstract class <!CYCLIC_SCOPES_WITH_COMPANION!>DerivedAbstract<!> : C.Base()
class Data
public class C {
open class <!CYCLIC_SCOPES_WITH_COMPANION!>Base<!>() {
// error-scope
val foo: <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>Int<!> = 42
}
// error-scope
val data: <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>Data<!> = Data()
companion <!CYCLIC_SCOPES_WITH_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 <!CYCLIC_SCOPES_WITH_COMPANION!>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 <!CYCLIC_SCOPES_WITH_COMPANION!>Base<!>() {
// Now it is unresolved (vs. ErrorType like in the previous case)
val foo: <!UNRESOLVED_REFERENCE!>Int<!> = 42
}
companion <!CYCLIC_SCOPES_WITH_COMPANION!>object<!> : DerivedAbstract()
}
}
@@ -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 : <ERROR CLASS: 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 final fun foo(): [ERROR : <ERROR CLASS: Int>]
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 : <ERROR CLASS: Int>]
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 : <ERROR CLASS: Int>]
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 : <ERROR CLASS: 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 : <ERROR CLASS: 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 : WithPropertyInBase.DerivedAbstract {
private constructor Companion()
public final override /*1*/ /*fake_override*/ val foo: [ERROR : <ERROR CLASS: 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 : WithPropertyInBase.C.Base {
public constructor DerivedAbstract()
public final override /*1*/ /*fake_override*/ val foo: [ERROR : <ERROR CLASS: 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 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
}
}
@@ -0,0 +1,17 @@
// see https://youtrack.jetbrains.com/issue/KT-21515
abstract class <!CYCLIC_SCOPES_WITH_COMPANION!>DerivedAbstract<!> : C.Base()
class Data
open class C {
open class <!CYCLIC_SCOPES_WITH_COMPANION!>Base<!> {
open fun m() {}
}
val field = Data()
companion <!CYCLIC_SCOPES_WITH_COMPANION!>object<!> : DerivedAbstract() {
override fun m() {}
}
}
@@ -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
}
@@ -1,11 +1,11 @@
class Test {
@`InnerAnnotation` <!REPEATED_ANNOTATION!>@InnerAnnotation<!>
companion object : StaticClass(), <!UNRESOLVED_REFERENCE, MANY_CLASSES_IN_SUPERTYPE_LIST, DEBUG_INFO_UNRESOLVED_WITH_TARGET!>InnerClass<!>() {
companion <!CYCLIC_SCOPES_WITH_COMPANION, CYCLIC_SCOPES_WITH_COMPANION!>object<!> : StaticClass(), <!UNRESOLVED_REFERENCE, MANY_CLASSES_IN_SUPERTYPE_LIST, DEBUG_INFO_UNRESOLVED_WITH_TARGET!>InnerClass<!>() {
}
annotation class InnerAnnotation
open class StaticClass
open class <!CYCLIC_SCOPES_WITH_COMPANION!>StaticClass<!>
open inner class InnerClass
open inner class <!CYCLIC_SCOPES_WITH_COMPANION!>InnerClass<!>
}
@@ -5,7 +5,7 @@ class TestSome<P> {
}
class Test {
companion object : <!UNRESOLVED_REFERENCE, DEBUG_INFO_UNRESOLVED_WITH_TARGET!>InnerClass<!>() {
companion <!CYCLIC_SCOPES_WITH_COMPANION!>object<!> : <!UNRESOLVED_REFERENCE, DEBUG_INFO_UNRESOLVED_WITH_TARGET!>InnerClass<!>() {
val a = object: <!UNRESOLVED_REFERENCE, DEBUG_INFO_UNRESOLVED_WITH_TARGET!>InnerClass<!>() {
}
@@ -22,5 +22,5 @@ class Test {
val inClass = 12
fun foo() {}
open inner class InnerClass
open inner class <!CYCLIC_SCOPES_WITH_COMPANION!>InnerClass<!>
}
@@ -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")
@@ -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")
@@ -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<KotlinType> getAdditionalNeighboursInSupertypeGraph() {
protected Collection<KotlinType> getAdditionalNeighboursInSupertypeGraph(boolean useCompanions) {
DeclarationDescriptor containingDeclaration = getDeclarationDescriptor().getContainingDeclaration();
if (!(containingDeclaration instanceof ClassDescriptor)) {
return Collections.emptyList();
}
Collection<KotlinType> additionalNeighbours = new SmartList<KotlinType>();
// 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.<KotlinType>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;
}
}
@@ -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<KotlinType>) ?: resultWithoutCycles.toList()
})
private fun TypeConstructor.computeNeighbours(): Collection<KotlinType> =
private fun TypeConstructor.computeNeighbours(useCompanions: Boolean): Collection<KotlinType> =
(this as? AbstractTypeConstructor)?.let {
abstractClassifierDescriptor ->
abstractClassifierDescriptor.supertypes().allSupertypes +
abstractClassifierDescriptor.getAdditionalNeighboursInSupertypeGraph()
abstractClassifierDescriptor.getAdditionalNeighboursInSupertypeGraph(useCompanions)
} ?: supertypes
protected abstract fun computeSupertypes(): Collection<KotlinType>
protected abstract val supertypeLoopChecker: SupertypeLoopChecker
protected open fun reportSupertypeLoopError(type: KotlinType) {}
protected open fun getAdditionalNeighboursInSupertypeGraph(): Collection<KotlinType> = emptyList()
// TODO: overload in AbstractTypeParameterDescriptor?
protected open fun reportScopesLoopError(type: KotlinType) {}
protected open fun getAdditionalNeighboursInSupertypeGraph(useCompanions: Boolean): Collection<KotlinType> = emptyList()
protected open fun defaultSupertypeIfEmpty(): KotlinType? = null
}