Consider outer classes when topsorting hierarchy
- when building a topsorted hierarchy, consider not only edges to the class' superclasses, but also from inner/nested classes to their container. This effectively means that a class cannot inherit from its nested class. Similar cases (see the tests) are disallowed in Java. - filter classes by their presence in TopDownAnalysisContext, not by being a MutableClassDescriptor (otherwise analysis for local classes ends up trying to do something with outer superclasses) - delete outdated comment: topsort is needed not only for better diagnostics, but also for correct order of fake overrides construction
This commit is contained in:
@@ -16,10 +16,8 @@
|
||||
|
||||
package org.jetbrains.jet.lang.resolve;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
import kotlin.KotlinPackage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
@@ -32,7 +30,6 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WriteThroughScope;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeConstructor;
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||
import org.jetbrains.jet.utils.DFS;
|
||||
|
||||
@@ -49,6 +46,25 @@ import static org.jetbrains.jet.lang.resolve.ModifiersChecker.resolveVisibilityF
|
||||
import static org.jetbrains.jet.lang.resolve.name.SpecialNames.getClassObjectName;
|
||||
|
||||
public class TypeHierarchyResolver {
|
||||
private static final DFS.Neighbors<ClassDescriptor> CLASS_INHERITANCE_EDGES = new DFS.Neighbors<ClassDescriptor>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterable<ClassDescriptor> getNeighbors(ClassDescriptor current) {
|
||||
List<ClassDescriptor> result = new ArrayList<ClassDescriptor>();
|
||||
for (JetType supertype : current.getDefaultType().getConstructor().getSupertypes()) {
|
||||
DeclarationDescriptor descriptor = supertype.getConstructor().getDeclarationDescriptor();
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
result.add((ClassDescriptor) descriptor);
|
||||
}
|
||||
}
|
||||
DeclarationDescriptor container = current.getContainingDeclaration();
|
||||
if (container instanceof ClassDescriptor) {
|
||||
result.add((ClassDescriptor) container);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
private ImportsResolver importsResolver;
|
||||
@NotNull
|
||||
@@ -220,28 +236,12 @@ public class TypeHierarchyResolver {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<MutableClassDescriptor> topologicallySortClassesAndObjects(@NotNull TopDownAnalysisContext c) {
|
||||
// A topsort is needed only for better diagnostics:
|
||||
// edges that get removed to disconnect loops are more reasonable in this case
|
||||
//noinspection unchecked
|
||||
List<ClassDescriptor> orderedClasses = DFS.topologicalOrder(
|
||||
(Iterable) c.getAllClasses(),
|
||||
new DFS.Neighbors<ClassDescriptor>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterable<ClassDescriptor> getNeighbors(ClassDescriptor current) {
|
||||
List<ClassDescriptor> result = Lists.newArrayList();
|
||||
for (JetType supertype : current.getDefaultType().getConstructor().getSupertypes()) {
|
||||
DeclarationDescriptor descriptor = supertype.getConstructor().getDeclarationDescriptor();
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
result.add((ClassDescriptor) descriptor);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
);
|
||||
return KotlinPackage.filterIsInstance(orderedClasses, MutableClassDescriptor.class);
|
||||
Collection<ClassDescriptor> sourceClasses = (Collection) c.getAllClasses();
|
||||
List<ClassDescriptor> allClassesOrdered = DFS.topologicalOrder(sourceClasses, CLASS_INHERITANCE_EDGES);
|
||||
allClassesOrdered.retainAll(sourceClasses);
|
||||
return (List) allClassesOrdered;
|
||||
}
|
||||
|
||||
private void detectAndDisconnectLoops(@NotNull TopDownAnalysisContext c) {
|
||||
@@ -269,22 +269,15 @@ public class TypeHierarchyResolver {
|
||||
}
|
||||
}
|
||||
|
||||
// Temporary. Duplicates logic from LazyClassTypeConstructor.isReachable
|
||||
// TODO: use DFS and copy to LazyClassTypeConstructor.isReachable
|
||||
private static boolean isReachable(
|
||||
@NotNull ClassDescriptor from,
|
||||
@NotNull MutableClassDescriptor to,
|
||||
@NotNull Set<ClassDescriptor> visited
|
||||
) {
|
||||
if (!visited.add(from)) return false;
|
||||
for (JetType supertype : from.getDefaultType().getConstructor().getSupertypes()) {
|
||||
TypeConstructor supertypeConstructor = supertype.getConstructor();
|
||||
if (supertypeConstructor.getDeclarationDescriptor() == to) {
|
||||
return true;
|
||||
}
|
||||
ClassifierDescriptor superclass = supertypeConstructor.getDeclarationDescriptor();
|
||||
if (superclass instanceof ClassDescriptor && isReachable((ClassDescriptor) superclass, to, visited)) {
|
||||
return true;
|
||||
}
|
||||
for (ClassDescriptor superclass : CLASS_INHERITANCE_EDGES.getNeighbors(from)) {
|
||||
if (superclass == to || isReachable(superclass, to, visited)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
open class C : <!CYCLIC_INHERITANCE_HIERARCHY!>D<!>() {
|
||||
open class CC
|
||||
}
|
||||
open class D : <!CYCLIC_INHERITANCE_HIERARCHY!>C.CC<!>()
|
||||
@@ -0,0 +1,3 @@
|
||||
open class E : <!CYCLIC_INHERITANCE_HIERARCHY!>E.EE<!>() {
|
||||
open class EE
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// FILE: ExceptionTracker.kt
|
||||
|
||||
trait ExceptionTracker : <!CYCLIC_INHERITANCE_HIERARCHY!>LockBasedStorageManager.ExceptionHandlingStrategy<!> {
|
||||
}
|
||||
|
||||
// FILE: StorageManager.kt
|
||||
|
||||
trait StorageManager : <!CYCLIC_INHERITANCE_HIERARCHY!>ExceptionTracker<!> {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
// FILE: LockBasedStorageManager.java
|
||||
|
||||
class LockBasedStorageManager extends StorageManager {
|
||||
interface ExceptionHandlingStrategy {
|
||||
void bar();
|
||||
}
|
||||
|
||||
@Override
|
||||
void foo() {}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
open class A : <!CYCLIC_INHERITANCE_HIERARCHY!>B.BB<!>() {
|
||||
open class AA
|
||||
}
|
||||
open class B : <!CYCLIC_INHERITANCE_HIERARCHY!>A.AA<!>() {
|
||||
open class BB
|
||||
}
|
||||
@@ -1823,6 +1823,16 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/cyclicHierarchy"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("classIndirectlyInheritsNested.kt")
|
||||
public void testClassIndirectlyInheritsNested() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/classIndirectlyInheritsNested.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classInheritsNested.kt")
|
||||
public void testClassInheritsNested() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/classInheritsNested.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("cyclicHierarchy.kt")
|
||||
public void testCyclicHierarchy() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/cyclicHierarchy.kt");
|
||||
@@ -1843,11 +1853,21 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/kotlinJavaKotlinCycle.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kotlinJavaNestedCycle.kt")
|
||||
public void testKotlinJavaNestedCycle() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/kotlinJavaNestedCycle.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt303.kt")
|
||||
public void testKt303() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/kt303.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("twoClassesWithNestedCycle.kt")
|
||||
public void testTwoClassesWithNestedCycle() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/twoClassesWithNestedCycle.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/dataClasses")
|
||||
|
||||
Reference in New Issue
Block a user