Simplify DefaultImportProvider, introduce "low priority imports"
Previously, packages `java.lang` and `kotlin.jvm` were imported on JVM by default on the same rights, causing problems when the same classifier existed both in `java.lang` and `kotlin.jvm`. Since the only known case of such conflict were type aliases to JVM classes, the corresponding classes (expansions of those type aliases) were manually excluded from default imports. This made the code in DefaultImportProvider complicated and resulted in multiple problems, regarding both correctness and performance (see82364ad3e5,a9f2f5c7d0,dd3dbda719). This change adds a new concept, a "low priority import", and treats `java.lang` as such. Since these imports are now separated from the rest of default imports in LazyImportScope via secondaryClassImportResolver, conflicts between classifiers are handled naturally: the one from `kotlin.jvm` always wins (unless the one from `java.lang` is imported explicitly, of course). This approach is simpler, safer and does not require any memory to cache anything. Skip ResolveToJava.kt test for javac-based resolve; it now fails because of a weird issue which I didn't have time to investigate (this is OK because it's a corner case of an experimental functionality)
This commit is contained in:
@@ -29,7 +29,6 @@ object JvmPlatform : TargetPlatform("JVM") {
|
||||
ArrayList<ImportPath>().apply {
|
||||
addAll(Common.getDefaultImports(includeKotlinComparisons))
|
||||
|
||||
add(ImportPath.fromString("java.lang.*"))
|
||||
add(ImportPath.fromString("kotlin.jvm.*"))
|
||||
|
||||
fun addAllClassifiersFromScope(scope: MemberScope) {
|
||||
@@ -46,6 +45,8 @@ object JvmPlatform : TargetPlatform("JVM") {
|
||||
|
||||
}
|
||||
|
||||
override val defaultLowPriorityImports: List<ImportPath> = listOf(ImportPath.fromString("java.lang.*"))
|
||||
|
||||
override fun getDefaultImports(includeKotlinComparisons: Boolean): List<ImportPath> = defaultImports(includeKotlinComparisons)
|
||||
|
||||
override val platformConfigurator: PlatformConfigurator = JvmPlatformConfigurator
|
||||
|
||||
@@ -24,7 +24,11 @@ abstract class TargetPlatform(val platformName: String) {
|
||||
override fun toString() = platformName
|
||||
|
||||
abstract val platformConfigurator: PlatformConfigurator
|
||||
|
||||
open val defaultLowPriorityImports: List<ImportPath> get() = emptyList()
|
||||
|
||||
abstract fun getDefaultImports(includeKotlinComparisons: Boolean): List<ImportPath>
|
||||
|
||||
open val excludedImports: List<FqName> get() = emptyList()
|
||||
|
||||
abstract val multiTargetPlatform: MultiTargetPlatform
|
||||
|
||||
@@ -16,73 +16,24 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.lazy
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.DefaultImportOfPackageKotlinComparisons
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.isChildOf
|
||||
import org.jetbrains.kotlin.name.isSubpackageOf
|
||||
import org.jetbrains.kotlin.resolve.ImportPath
|
||||
import org.jetbrains.kotlin.resolve.SinceKotlinAccessibility
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.storage.getValue
|
||||
|
||||
class DefaultImportProvider(
|
||||
storageManager: StorageManager,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
private val targetPlatform: TargetPlatform,
|
||||
private val languageVersionSettings: LanguageVersionSettings
|
||||
) {
|
||||
companion object {
|
||||
private val PACKAGES_WITH_ALIASES = listOf(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME, KotlinBuiltIns.TEXT_PACKAGE_FQ_NAME)
|
||||
|
||||
private fun ModuleDescriptor.findTypeAliasesInPackages(packages: Collection<FqName>): Collection<TypeAliasDescriptor> {
|
||||
val result = mutableListOf<TypeAliasDescriptor>()
|
||||
|
||||
for (dependencyModuleDescriptor in allDependencyModules) {
|
||||
if (dependencyModuleDescriptor !is ModuleDescriptorImpl) continue
|
||||
|
||||
for (packageFqName in packages) {
|
||||
dependencyModuleDescriptor.packageFragmentProviderForContent.getPackageFragments(packageFqName)
|
||||
.flatMapTo(result) { packageFragmentDescriptor ->
|
||||
packageFragmentDescriptor.getMemberScope()
|
||||
.getContributedDescriptors(DescriptorKindFilter.TYPE_ALIASES)
|
||||
.filterIsInstance<TypeAliasDescriptor>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
val defaultImports: List<ImportPath> by storageManager.createLazyValue {
|
||||
targetPlatform.getDefaultImports(languageVersionSettings.supportsFeature(DefaultImportOfPackageKotlinComparisons))
|
||||
}
|
||||
|
||||
val excludedImports: List<FqName> by storageManager.createLazyValue {
|
||||
val builtinTypeAliases =
|
||||
moduleDescriptor.findTypeAliasesInPackages(PACKAGES_WITH_ALIASES)
|
||||
.filter { it.checkSinceKotlinVersionAccessibility(languageVersionSettings) == SinceKotlinAccessibility.Accessible }
|
||||
|
||||
val nonKotlinDefaultImportedPackages =
|
||||
defaultImports
|
||||
.filter { it.isAllUnder }
|
||||
.mapNotNull {
|
||||
it.fqName.takeUnless { it.isSubpackageOf(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME) }
|
||||
}
|
||||
val nonKotlinAliasedTypeFqNames =
|
||||
builtinTypeAliases
|
||||
.mapNotNull { it.expandedType.constructor.declarationDescriptor?.fqNameSafe }
|
||||
.filter { nonKotlinDefaultImportedPackages.any(it::isChildOf) }
|
||||
|
||||
nonKotlinAliasedTypeFqNames + targetPlatform.excludedImports
|
||||
val allDefaultImports: List<ImportPath> by storageManager.createLazyValue {
|
||||
defaultImports + targetPlatform.defaultLowPriorityImports
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ class FileScopeFactory(
|
||||
private val ktImportsFactory: KtImportsFactory,
|
||||
private val platformToKotlinClassMap: PlatformToKotlinClassMap,
|
||||
private val defaultImportProvider: DefaultImportProvider,
|
||||
private val targetPlatform: TargetPlatform,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
private val deprecationResolver: DeprecationResolver
|
||||
) {
|
||||
@@ -53,6 +54,10 @@ class FileScopeFactory(
|
||||
ktImportsFactory.createImportDirectivesNotCached(defaultImportProvider.defaultImports)
|
||||
}
|
||||
|
||||
private val defaultLowPriorityImports by storageManager.createLazyValue {
|
||||
ktImportsFactory.createImportDirectivesNotCached(targetPlatform.defaultLowPriorityImports)
|
||||
}
|
||||
|
||||
fun createScopesForFile(file: KtFile, existingImports: ImportingScope? = null): FileScopes {
|
||||
val packageView = moduleDescriptor.getPackage(file.packageFqName)
|
||||
val packageFragment = topLevelDescriptorProvider.getPackageFragmentOrDiagnoseFailure(file.packageFqName, file)
|
||||
@@ -60,10 +65,16 @@ class FileScopeFactory(
|
||||
return FilesScopesBuilder(file, existingImports, packageFragment, packageView).result
|
||||
}
|
||||
|
||||
private data class DefaultImportResolvers(
|
||||
val explicit: LazyImportResolver,
|
||||
val allUnder: LazyImportResolver,
|
||||
val lowPriority: LazyImportResolver
|
||||
)
|
||||
|
||||
private fun createDefaultImportResolvers(
|
||||
extraImports: Collection<KtImportDirective>,
|
||||
aliasImportNames: Collection<FqName>
|
||||
): Pair<LazyImportResolver, LazyImportResolver> {
|
||||
): DefaultImportResolvers {
|
||||
val tempTrace = TemporaryBindingTrace.create(bindingTrace, "Transient trace for default imports lazy resolve", false)
|
||||
val allImplicitImports = defaultImports concat extraImports
|
||||
|
||||
@@ -73,22 +84,29 @@ class FileScopeFactory(
|
||||
allImplicitImports.filter { it.isAllUnder || it.importedFqName !in aliasImportNames }
|
||||
}
|
||||
|
||||
val defaultExplicitImportResolver = createImportResolver(
|
||||
val explicit = createImportResolver(
|
||||
ExplicitImportsIndexed(defaultImportsFiltered),
|
||||
tempTrace,
|
||||
packageFragment = null,
|
||||
aliasImportNames = aliasImportNames
|
||||
)
|
||||
val defaultAllUnderImportResolver =
|
||||
createImportResolver(
|
||||
AllUnderImportsIndexed(defaultImportsFiltered),
|
||||
tempTrace,
|
||||
packageFragment = null,
|
||||
aliasImportNames = aliasImportNames,
|
||||
excludedImports = defaultImportProvider.excludedImports
|
||||
)
|
||||
val allUnder = createImportResolver(
|
||||
AllUnderImportsIndexed(defaultImportsFiltered),
|
||||
tempTrace,
|
||||
packageFragment = null,
|
||||
aliasImportNames = aliasImportNames,
|
||||
excludedImports = targetPlatform.excludedImports
|
||||
)
|
||||
val lowPriority = createImportResolver(
|
||||
AllUnderImportsIndexed(defaultLowPriorityImports.also { imports ->
|
||||
assert(imports.all { it.isAllUnder }) { "All low priority imports must be all-under: $imports" }
|
||||
}),
|
||||
tempTrace,
|
||||
packageFragment = null,
|
||||
aliasImportNames = aliasImportNames
|
||||
)
|
||||
|
||||
return defaultExplicitImportResolver to defaultAllUnderImportResolver
|
||||
return DefaultImportResolvers(explicit, allUnder, lowPriority)
|
||||
}
|
||||
|
||||
private val defaultImportResolvers by storageManager.createLazyValue {
|
||||
@@ -152,7 +170,7 @@ class FileScopeFactory(
|
||||
|
||||
val result = FileScopes(lexicalScope, lazyImportingScope, importResolver)
|
||||
|
||||
private fun createDefaultImportResolversForFile(): Pair<LazyImportResolver, LazyImportResolver> {
|
||||
private fun createDefaultImportResolversForFile(): DefaultImportResolvers {
|
||||
val extraImports = file.takeIf { it.isScript() }?.originalFile?.virtualFile?.let { vFile ->
|
||||
val scriptExternalDependencies = getScriptExternalDependencies(vFile, file.project)
|
||||
ktImportsFactory.createImportDirectives(scriptExternalDependencies?.imports?.map { ImportPath.fromString(it) }.orEmpty())
|
||||
@@ -166,37 +184,40 @@ class FileScopeFactory(
|
||||
}
|
||||
|
||||
fun createImportingScope(): LazyImportScope {
|
||||
val (defaultExplicitImportResolver, defaultAllUnderImportResolver) = createDefaultImportResolversForFile()
|
||||
val (defaultExplicitImportResolver, defaultAllUnderImportResolver, defaultLowPriorityImportResolver) =
|
||||
createDefaultImportResolversForFile()
|
||||
|
||||
val dummyContainerDescriptor = DummyContainerDescriptor(file, packageFragment)
|
||||
|
||||
var scope: ImportingScope
|
||||
|
||||
val debugName = "LazyFileScope for file " + file.name
|
||||
|
||||
scope = LazyImportScope(
|
||||
existingImports, defaultAllUnderImportResolver, LazyImportScope.FilteringKind.INVISIBLE_CLASSES,
|
||||
existingImports, defaultAllUnderImportResolver, defaultLowPriorityImportResolver,
|
||||
LazyImportScope.FilteringKind.INVISIBLE_CLASSES,
|
||||
"Default all under imports in $debugName (invisible classes only)"
|
||||
)
|
||||
|
||||
scope = LazyImportScope(
|
||||
scope, allUnderImportResolver, LazyImportScope.FilteringKind.INVISIBLE_CLASSES,
|
||||
scope, allUnderImportResolver, null, LazyImportScope.FilteringKind.INVISIBLE_CLASSES,
|
||||
"All under imports in $debugName (invisible classes only)"
|
||||
)
|
||||
|
||||
scope = currentPackageScope(packageView, aliasImportNames, dummyContainerDescriptor, FilteringKind.INVISIBLE_CLASSES, scope)
|
||||
|
||||
scope = LazyImportScope(
|
||||
scope, defaultAllUnderImportResolver, LazyImportScope.FilteringKind.VISIBLE_CLASSES,
|
||||
scope, defaultAllUnderImportResolver, defaultLowPriorityImportResolver, LazyImportScope.FilteringKind.VISIBLE_CLASSES,
|
||||
"Default all under imports in $debugName (visible classes)"
|
||||
)
|
||||
|
||||
scope = LazyImportScope(
|
||||
scope, allUnderImportResolver, LazyImportScope.FilteringKind.VISIBLE_CLASSES,
|
||||
scope, allUnderImportResolver, null, LazyImportScope.FilteringKind.VISIBLE_CLASSES,
|
||||
"All under imports in $debugName (visible classes)"
|
||||
)
|
||||
|
||||
scope = LazyImportScope(
|
||||
scope, defaultExplicitImportResolver, LazyImportScope.FilteringKind.ALL,
|
||||
scope, defaultExplicitImportResolver, null, LazyImportScope.FilteringKind.ALL,
|
||||
"Default explicit imports in $debugName"
|
||||
)
|
||||
|
||||
@@ -204,7 +225,7 @@ class FileScopeFactory(
|
||||
|
||||
scope = currentPackageScope(packageView, aliasImportNames, dummyContainerDescriptor, FilteringKind.VISIBLE_CLASSES, scope)
|
||||
|
||||
return LazyImportScope(scope, explicitImportResolver, LazyImportScope.FilteringKind.ALL, "Explicit imports in $debugName")
|
||||
return LazyImportScope(scope, explicitImportResolver, null, LazyImportScope.FilteringKind.ALL, "Explicit imports in $debugName")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -228,7 +249,7 @@ class FileScopeFactory(
|
||||
return object : ImportingScope {
|
||||
override val parent: ImportingScope? = parentScope
|
||||
|
||||
override fun getContributedPackage(name: Name) = null
|
||||
override fun getContributedPackage(name: Name): Nothing? = null
|
||||
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
|
||||
if (name in excludedNames) return null
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.collectionUtils.concat
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.flatMapToNullable
|
||||
import java.util.*
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
|
||||
interface IndexedImports {
|
||||
val imports: List<KtImportDirective>
|
||||
@@ -208,6 +208,7 @@ class LazyImportResolver(
|
||||
class LazyImportScope(
|
||||
override val parent: ImportingScope?,
|
||||
private val importResolver: LazyImportResolver,
|
||||
private val secondaryImportResolver: LazyImportResolver?,
|
||||
private val filteringKind: LazyImportScope.FilteringKind,
|
||||
private val debugName: String
|
||||
) : ImportingScope {
|
||||
@@ -218,19 +219,23 @@ class LazyImportScope(
|
||||
INVISIBLE_CLASSES
|
||||
}
|
||||
|
||||
private fun isClassifierVisible(descriptor: ClassifierDescriptor): Boolean {
|
||||
private fun LazyImportResolver.isClassifierVisible(descriptor: ClassifierDescriptor): Boolean {
|
||||
if (filteringKind == FilteringKind.ALL) return true
|
||||
|
||||
if (importResolver.deprecationResolver.isHiddenInResolution(descriptor)) return false
|
||||
if (deprecationResolver.isHiddenInResolution(descriptor)) return false
|
||||
|
||||
val visibility = (descriptor as DeclarationDescriptorWithVisibility).visibility
|
||||
val includeVisible = filteringKind == FilteringKind.VISIBLE_CLASSES
|
||||
if (!visibility.mustCheckInImports()) return includeVisible
|
||||
return Visibilities.isVisibleIgnoringReceiver(descriptor, importResolver.moduleDescriptor) == includeVisible
|
||||
return Visibilities.isVisibleIgnoringReceiver(descriptor, moduleDescriptor) == includeVisible
|
||||
}
|
||||
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
|
||||
return importResolver.selectSingleFromImports(name) { scope, name ->
|
||||
return importResolver.getClassifier(name, location) ?: secondaryImportResolver?.getClassifier(name, location)
|
||||
}
|
||||
|
||||
private fun LazyImportResolver.getClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
|
||||
return selectSingleFromImports(name) { scope, _ ->
|
||||
val descriptor = scope.getContributedClassifier(name, location)
|
||||
if ((descriptor is ClassDescriptor || descriptor is TypeAliasDescriptor) && isClassifierVisible(descriptor))
|
||||
descriptor
|
||||
@@ -239,16 +244,20 @@ class LazyImportScope(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContributedPackage(name: Name) = null
|
||||
override fun getContributedPackage(name: Name): PackageViewDescriptor? = null
|
||||
|
||||
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<VariableDescriptor> {
|
||||
if (filteringKind == FilteringKind.INVISIBLE_CLASSES) return listOf()
|
||||
return importResolver.collectFromImports(name) { scope, name -> scope.getContributedVariables(name, location) }
|
||||
return importResolver.collectFromImports(name) { scope, _ -> scope.getContributedVariables(name, location) }.ifEmpty {
|
||||
secondaryImportResolver?.collectFromImports(name) { scope, _ -> scope.getContributedVariables(name, location) }.orEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<FunctionDescriptor> {
|
||||
if (filteringKind == FilteringKind.INVISIBLE_CLASSES) return listOf()
|
||||
return importResolver.collectFromImports(name) { scope, name -> scope.getContributedFunctions(name, location) }
|
||||
return importResolver.collectFromImports(name) { scope, _ -> scope.getContributedFunctions(name, location) }.ifEmpty {
|
||||
secondaryImportResolver?.collectFromImports(name) { scope, _ -> scope.getContributedFunctions(name, location) }.orEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContributedDescriptors(
|
||||
@@ -259,26 +268,49 @@ class LazyImportScope(
|
||||
// we do not perform any filtering by visibility here because all descriptors from both visible/invisible filter scopes are to be added anyway
|
||||
if (filteringKind == FilteringKind.INVISIBLE_CLASSES) return listOf()
|
||||
|
||||
return importResolver.storageManager.compute {
|
||||
val descriptors = LinkedHashSet<DeclarationDescriptor>()
|
||||
val storageManager = importResolver.storageManager
|
||||
if (secondaryImportResolver != null) {
|
||||
assert(storageManager === secondaryImportResolver.storageManager) { "Multiple storage managers are not supported" }
|
||||
}
|
||||
|
||||
return storageManager.compute {
|
||||
val result = linkedSetOf<DeclarationDescriptor>()
|
||||
val importedNames = if (secondaryImportResolver == null) null else hashSetOf<Name>()
|
||||
|
||||
for (directive in importResolver.indexedImports.imports) {
|
||||
val importPath = directive.importPath ?: continue
|
||||
val importedName = importPath.importedName
|
||||
if (importedName == null || nameFilter(importedName)) {
|
||||
descriptors.addAll(
|
||||
importResolver.getImportScope(directive).getContributedDescriptors(
|
||||
kindFilter,
|
||||
nameFilter,
|
||||
changeNamesForAliased
|
||||
)
|
||||
)
|
||||
val newDescriptors =
|
||||
importResolver.getImportScope(directive).getContributedDescriptors(kindFilter, nameFilter, changeNamesForAliased)
|
||||
result.addAll(newDescriptors)
|
||||
|
||||
if (importedNames != null) {
|
||||
for (descriptor in newDescriptors) {
|
||||
importedNames.add(descriptor.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
descriptors
|
||||
|
||||
secondaryImportResolver?.let { resolver ->
|
||||
for (directive in resolver.indexedImports.imports) {
|
||||
val newDescriptors =
|
||||
resolver.getImportScope(directive).getContributedDescriptors(kindFilter, nameFilter, changeNamesForAliased)
|
||||
|
||||
for (descriptor in newDescriptors) {
|
||||
if (descriptor.name !in importedNames!!) {
|
||||
result.add(descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = "LazyImportScope: " + debugName
|
||||
override fun toString() = "LazyImportScope: $debugName"
|
||||
|
||||
override fun printStructure(p: Printer) {
|
||||
p.println(this::class.java.simpleName, ": ", debugName, " {")
|
||||
@@ -288,11 +320,13 @@ class LazyImportScope(
|
||||
p.println("}")
|
||||
}
|
||||
|
||||
override fun definitelyDoesNotContainName(name: Name) = importResolver.definitelyDoesNotContainName(name)
|
||||
override fun definitelyDoesNotContainName(name: Name): Boolean =
|
||||
importResolver.definitelyDoesNotContainName(name) && secondaryImportResolver?.definitelyDoesNotContainName(name) != false
|
||||
|
||||
override fun recordLookup(name: Name, location: LookupLocation) {
|
||||
importResolver.recordLookup(name, location)
|
||||
secondaryImportResolver?.recordLookup(name, location)
|
||||
}
|
||||
|
||||
override fun computeImportedNames() = importResolver.allNames
|
||||
override fun computeImportedNames(): Set<Name>? = importResolver.allNames?.union(secondaryImportResolver?.allNames.orEmpty())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
// !CHECK_TYPE
|
||||
// JAVAC_SKIP
|
||||
|
||||
// FILE: f.kt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user