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;
|
package org.jetbrains.jet.lang.resolve;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||||
import kotlin.KotlinPackage;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.descriptors.*;
|
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.WritableScope;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.WriteThroughScope;
|
import org.jetbrains.jet.lang.resolve.scopes.WriteThroughScope;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
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.lang.types.lang.KotlinBuiltIns;
|
||||||
import org.jetbrains.jet.utils.DFS;
|
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;
|
import static org.jetbrains.jet.lang.resolve.name.SpecialNames.getClassObjectName;
|
||||||
|
|
||||||
public class TypeHierarchyResolver {
|
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
|
@NotNull
|
||||||
private ImportsResolver importsResolver;
|
private ImportsResolver importsResolver;
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -220,28 +236,12 @@ public class TypeHierarchyResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private static List<MutableClassDescriptor> topologicallySortClassesAndObjects(@NotNull TopDownAnalysisContext c) {
|
private static List<MutableClassDescriptor> topologicallySortClassesAndObjects(@NotNull TopDownAnalysisContext c) {
|
||||||
// A topsort is needed only for better diagnostics:
|
Collection<ClassDescriptor> sourceClasses = (Collection) c.getAllClasses();
|
||||||
// edges that get removed to disconnect loops are more reasonable in this case
|
List<ClassDescriptor> allClassesOrdered = DFS.topologicalOrder(sourceClasses, CLASS_INHERITANCE_EDGES);
|
||||||
//noinspection unchecked
|
allClassesOrdered.retainAll(sourceClasses);
|
||||||
List<ClassDescriptor> orderedClasses = DFS.topologicalOrder(
|
return (List) allClassesOrdered;
|
||||||
(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void detectAndDisconnectLoops(@NotNull TopDownAnalysisContext c) {
|
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(
|
private static boolean isReachable(
|
||||||
@NotNull ClassDescriptor from,
|
@NotNull ClassDescriptor from,
|
||||||
@NotNull MutableClassDescriptor to,
|
@NotNull MutableClassDescriptor to,
|
||||||
@NotNull Set<ClassDescriptor> visited
|
@NotNull Set<ClassDescriptor> visited
|
||||||
) {
|
) {
|
||||||
if (!visited.add(from)) return false;
|
if (!visited.add(from)) return false;
|
||||||
for (JetType supertype : from.getDefaultType().getConstructor().getSupertypes()) {
|
for (ClassDescriptor superclass : CLASS_INHERITANCE_EDGES.getNeighbors(from)) {
|
||||||
TypeConstructor supertypeConstructor = supertype.getConstructor();
|
if (superclass == to || isReachable(superclass, to, visited)) return true;
|
||||||
if (supertypeConstructor.getDeclarationDescriptor() == to) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
ClassifierDescriptor superclass = supertypeConstructor.getDeclarationDescriptor();
|
|
||||||
if (superclass instanceof ClassDescriptor && isReachable((ClassDescriptor) superclass, to, visited)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
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);
|
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")
|
@TestMetadata("cyclicHierarchy.kt")
|
||||||
public void testCyclicHierarchy() throws Exception {
|
public void testCyclicHierarchy() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/cyclicHierarchy.kt");
|
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");
|
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")
|
@TestMetadata("kt303.kt")
|
||||||
public void testKt303() throws Exception {
|
public void testKt303() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/kt303.kt");
|
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")
|
@TestMetadata("compiler/testData/diagnostics/tests/dataClasses")
|
||||||
|
|||||||
Reference in New Issue
Block a user