Do not force resolve descriptors for explicit imports. Create lazy scope instead.

This commit is contained in:
Stanislav Erokhin
2017-02-06 20:31:42 +03:00
parent cc429cd865
commit b616ef0a40
25 changed files with 333 additions and 183 deletions
@@ -0,0 +1,117 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.*
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.BaseImportingScope
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.check
class LazyExplicitImportScope(
private val packageOrClassDescriptor: DeclarationDescriptor,
private val packageFragmentForVisibilityCheck: PackageFragmentDescriptor?,
private val declaredName: Name,
private val aliasName: Name,
private val storeReferences: (Collection<DeclarationDescriptor>) -> Unit
): BaseImportingScope(null) {
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
if (name != aliasName) return null
return when (packageOrClassDescriptor) {
is PackageViewDescriptor -> packageOrClassDescriptor.memberScope.getContributedClassifier(declaredName, location)
is ClassDescriptor -> packageOrClassDescriptor.unsubstitutedInnerClassesScope.getContributedClassifier(declaredName, location)
else -> throw IllegalStateException("Should be class or package: $packageOrClassDescriptor")
}
}
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<FunctionDescriptor> {
if (name != aliasName) return emptyList()
return collectCallableMemberDescriptors(location, MemberScope::getContributedFunctions)
}
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<VariableDescriptor> {
if (name != aliasName) return emptyList()
return collectCallableMemberDescriptors(location, MemberScope::getContributedVariables)
}
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> {
val descriptors = SmartList<DeclarationDescriptor>()
descriptors.addIfNotNull(getContributedClassifier(aliasName, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS))
descriptors.addAll(getContributedFunctions(aliasName, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS))
descriptors.addAll(getContributedVariables(aliasName, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS))
return descriptors
}
override fun printStructure(p: Printer) {
p.println(javaClass.simpleName, ": ", aliasName)
}
// should be called only once
internal fun storeReferencesToDescriptors() = getContributedDescriptors().apply(storeReferences)
private fun <D : CallableMemberDescriptor> collectCallableMemberDescriptors(
location: LookupLocation,
getDescriptors: MemberScope.(Name, LookupLocation) -> Collection<D>
): Collection<D> {
val descriptors = SmartList<D>()
when (packageOrClassDescriptor) {
is PackageViewDescriptor -> {
val packageScope = packageOrClassDescriptor.memberScope
descriptors.addAll(packageScope.getDescriptors(declaredName, location))
}
is ClassDescriptor -> {
val staticClassScope = packageOrClassDescriptor.staticScope
descriptors.addAll(staticClassScope.getDescriptors(declaredName, location))
if (packageOrClassDescriptor.kind == ClassKind.OBJECT) {
descriptors.addAll(
packageOrClassDescriptor.unsubstitutedMemberScope.getDescriptors(declaredName, location)
.mapNotNull { it.asImportedFromObjectIfPossible() }
)
}
}
else -> throw IllegalStateException("Should be class or package: $packageOrClassDescriptor")
}
return descriptors.choseOnlyVisibleOrAll()
}
@Suppress("UNCHECKED_CAST")
private fun <D : CallableMemberDescriptor> D.asImportedFromObjectIfPossible(): D? = when (this) {
is PropertyDescriptor -> asImportedFromObject() as D
is FunctionDescriptor -> asImportedFromObject() as D
else -> null
}
private fun <D : CallableMemberDescriptor> Collection<D>.choseOnlyVisibleOrAll() =
filter { isVisible(it, packageFragmentForVisibilityCheck, position = QualifierPosition.IMPORT) }.
check { it.isNotEmpty() } ?: this
}
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import org.jetbrains.kotlin.types.expressions.isWithoutValueArguments
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.check
class QualifiedExpressionResolver {
@@ -107,7 +106,7 @@ class QualifiedExpressionResolver {
val lastPart = qualifierPartList.last()
val classifier = when (qualifier) {
is PackageViewDescriptor -> qualifier.memberScope.getContributedClassifier(lastPart.name, lastPart.location)
is ClassDescriptor -> {
is ClassDescriptor -> {
val descriptor = qualifier.unsubstitutedInnerClassesScope.getContributedClassifier(lastPart.name, lastPart.location)
checkNotEnumEntry(descriptor, trace, lastPart.expression)
descriptor
@@ -207,7 +206,7 @@ class QualifiedExpressionResolver {
path: List<QualifierPart>,
lastPart: QualifierPart,
packageFragmentForVisibilityCheck: PackageFragmentDescriptor?
): SingleImportScope? {
): ImportingScope? {
val aliasName = KtPsiUtil.getAliasName(importDirective)
if (aliasName == null) {
// import kotlin.
@@ -220,58 +219,16 @@ class QualifiedExpressionResolver {
packageFragmentForVisibilityCheck, scopeForFirstPart = null, position = QualifierPosition.IMPORT
) ?: return null
val candidates = collectCandidateDescriptors(lastPart, packageOrClassDescriptor)
if (candidates.isNotEmpty()) {
storeResult(trace, lastPart.expression, candidates, packageFragmentForVisibilityCheck, position = QualifierPosition.IMPORT, isQualifier = false)
}
else {
tryResolveDescriptorsWhichCannotBeImported(trace, moduleDescriptor, packageOrClassDescriptor, lastPart)
return null
}
return LazyExplicitImportScope(packageOrClassDescriptor, packageFragmentForVisibilityCheck, lastPart.name, aliasName) {
candidates ->
val importedDescriptors = candidates.filter { isVisible(it, packageFragmentForVisibilityCheck, position = QualifierPosition.IMPORT) }.
check { it.isNotEmpty() } ?: candidates
return SingleImportScope(aliasName, importedDescriptors)
}
private fun collectCandidateDescriptors(lastPart: QualifierPart, packageOrClassDescriptor: DeclarationDescriptor): SmartList<DeclarationDescriptor> {
val descriptors = SmartList<DeclarationDescriptor>()
val lastName = lastPart.name
val location = lastPart.location
when (packageOrClassDescriptor) {
is PackageViewDescriptor -> {
val packageScope = packageOrClassDescriptor.memberScope
descriptors.addIfNotNull(packageScope.getContributedClassifier(lastName, location))
descriptors.addAll(packageScope.getContributedVariables(lastName, location))
descriptors.addAll(packageScope.getContributedFunctions(lastName, location))
if (candidates.isNotEmpty()) {
storeResult(trace, lastPart.expression, candidates, packageFragmentForVisibilityCheck, position = QualifierPosition.IMPORT, isQualifier = false)
}
is ClassDescriptor -> {
descriptors.addIfNotNull(
packageOrClassDescriptor.unsubstitutedInnerClassesScope.getContributedClassifier(lastName, location)
)
val staticClassScope = packageOrClassDescriptor.staticScope
descriptors.addAll(staticClassScope.getContributedFunctions(lastName, location))
descriptors.addAll(staticClassScope.getContributedVariables(lastName, location))
if (packageOrClassDescriptor.kind == ClassKind.OBJECT) {
descriptors.addAll(
packageOrClassDescriptor.unsubstitutedMemberScope.getContributedFunctions(lastName, location)
.map { it.asImportedFromObject() }
)
descriptors.addAll(
packageOrClassDescriptor.unsubstitutedMemberScope.getContributedVariables(lastName, location)
.filterIsInstance<PropertyDescriptor>()
.map { it.asImportedFromObject() }
)
}
else {
tryResolveDescriptorsWhichCannotBeImported(trace, moduleDescriptor, packageOrClassDescriptor, lastPart)
}
else -> throw IllegalStateException("Should be class or package: $packageOrClassDescriptor")
}
return descriptors
}
private fun tryResolveDescriptorsWhichCannotBeImported(
@@ -344,9 +301,6 @@ class QualifiedExpressionResolver {
val location = KotlinLookupLocation(expression)
}
private enum class QualifierPosition {
PACKAGE_HEADER, IMPORT, TYPE, EXPRESSION
}
private fun resolveToPackageOrClass(
path: List<QualifierPart>,
@@ -403,7 +357,7 @@ class QualifiedExpressionResolver {
moduleDescriptor.quickResolveToPackage(path, trace, position)
var currentDescriptor: DeclarationDescriptor? = prefixDescriptor
for (qualifierPartIndex in nextIndexAfterPrefix .. path.size - 1) {
for (qualifierPartIndex in nextIndexAfterPrefix..path.size - 1) {
val qualifierPart = path[qualifierPartIndex]
val nextPackageOrClassDescriptor =
@@ -499,7 +453,7 @@ class QualifiedExpressionResolver {
)
if (result == null) return QualifiedExpressionResolveResult.UNRESOLVED
return when(index) {
return when (index) {
path.size -> QualifiedExpressionResolveResult(result, null)
path.size - 1 -> QualifiedExpressionResolveResult(result, path[index].name)
else -> QualifiedExpressionResolveResult.UNRESOLVED
@@ -640,12 +594,12 @@ class QualifiedExpressionResolver {
if (descriptor is DeclarationDescriptorWithVisibility) {
val fromToCheck =
if (shouldBeVisibleFrom is PackageFragmentDescriptor && shouldBeVisibleFrom.source == SourceElement.NO_SOURCE && referenceExpression.containingFile !is DummyHolder) {
PackageFragmentWithCustomSource(shouldBeVisibleFrom, KotlinSourceElement(referenceExpression.getContainingKtFile()))
}
else {
shouldBeVisibleFrom
}
if (shouldBeVisibleFrom is PackageFragmentDescriptor && shouldBeVisibleFrom.source == SourceElement.NO_SOURCE && referenceExpression.containingFile !is DummyHolder) {
PackageFragmentWithCustomSource(shouldBeVisibleFrom, KotlinSourceElement(referenceExpression.getContainingKtFile()))
}
else {
shouldBeVisibleFrom
}
if (!isVisible(descriptor, fromToCheck, position)) {
trace.report(Errors.INVISIBLE_REFERENCE.on(referenceExpression, descriptor, descriptor.visibility, descriptor))
}
@@ -671,21 +625,25 @@ class QualifiedExpressionResolver {
return qualifier
}
}
private fun isVisible(
descriptor: DeclarationDescriptor,
shouldBeVisibleFrom: DeclarationDescriptor?,
position: QualifierPosition
): Boolean {
if (descriptor !is DeclarationDescriptorWithVisibility || shouldBeVisibleFrom == null) return true
internal fun isVisible(
descriptor: DeclarationDescriptor,
shouldBeVisibleFrom: DeclarationDescriptor?,
position: QualifierPosition
): Boolean {
if (descriptor !is DeclarationDescriptorWithVisibility || shouldBeVisibleFrom == null) return true
val visibility = descriptor.visibility
if (position == QualifierPosition.IMPORT) {
if (Visibilities.isPrivate(visibility)) return false
if (!visibility.mustCheckInImports()) return true
}
return Visibilities.isVisibleIgnoringReceiver(descriptor, shouldBeVisibleFrom)
val visibility = descriptor.visibility
if (position == QualifierPosition.IMPORT) {
if (Visibilities.isPrivate(visibility)) return false
if (!visibility.mustCheckInImports()) return true
}
return Visibilities.isVisibleIgnoringReceiver(descriptor, shouldBeVisibleFrom)
}
internal enum class QualifierPosition {
PACKAGE_HEADER, IMPORT, TYPE, EXPRESSION
}
/*
@@ -1,45 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.BaseImportingScope
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.utils.Printer
class SingleImportScope(private val aliasName: Name, private val descriptors: Collection<DeclarationDescriptor>) : BaseImportingScope(null) {
override fun getContributedClassifier(name: Name, location: LookupLocation)
= if (name == aliasName) descriptors.filterIsInstance<ClassifierDescriptor>().singleOrNull() else null
override fun getContributedPackage(name: Name)
= if (name == aliasName) descriptors.filterIsInstance<PackageViewDescriptor>().singleOrNull() else null
override fun getContributedVariables(name: Name, location: LookupLocation)
= if (name == aliasName) descriptors.filterIsInstance<VariableDescriptor>() else emptyList()
override fun getContributedFunctions(name: Name, location: LookupLocation)
= if (name == aliasName) descriptors.filterIsInstance<FunctionDescriptor>() else emptyList()
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean)
= descriptors
override fun printStructure(p: Printer) {
p.println(javaClass.simpleName, ": ", aliasName)
}
}
@@ -88,13 +88,20 @@ class LazyImportResolver(
qualifiedExpressionResolver.processImportReference(
directive, moduleDescriptor, traceForImportResolve, excludedImportNames, packageFragment
)?.apply {
if (!directive.isAllUnder) {
PlatformClassesMappedToKotlinChecker.checkPlatformClassesMappedToKotlin(
platformToKotlinClassMap, traceForImportResolve, directive, getContributedDescriptors()
)
}
)
}
private val forceResolveImportDirective = storageManager.createMemoizedFunction {
directive: KtImportDirective ->
val scope = importedScopesProvider(directive)
if (scope is LazyExplicitImportScope) {
val allDescriptors = scope.storeReferencesToDescriptors()
PlatformClassesMappedToKotlinChecker.checkPlatformClassesMappedToKotlin(
platformToKotlinClassMap, traceForImportResolve, directive, allDescriptors
)
}
Unit
}
override fun forceResolveAllImports() {
@@ -140,7 +147,7 @@ class LazyImportResolver(
}
override fun forceResolveImport(importDirective: KtImportDirective) {
getImportScope(importDirective)
forceResolveImportDirective(importDirective)
}
fun <D : DeclarationDescriptor> selectSingleFromImports(