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:
@@ -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);
|
||||
|
||||
|
||||
+2
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-9
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user