Do not force resolve descriptors for explicit imports. Create lazy scope instead.
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user