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
@@ -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")