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
@@ -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()
}