fixup! [LL] WIP - Java package sets - Parent packages are now only considered for `hasPackage`. For example, the `kotlin` package was considered to be part of the combined symbol provider, which is not good, as only `kotlin.collections` and `kotlin.jvm.internal` contain Java classes.
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/LLFirJavaSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/LLFirJavaSymbolProvider.kt index c6da2fc..ef2aa5f 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/LLFirJavaSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/LLFirJavaSymbolProvider.kt
@@ -52,6 +52,10 @@ } ) + private val packageNamesWithParentPackages by lazy(LazyThreadSafetyMode.PUBLICATION) { + symbolNamesProvider.getPackageNames()?.let { computePackageNamesWithParentPackages(it) } + } + // TODO: Hacky! override val symbolNamesProvider: FirSymbolNamesProvider = FirDelegatingCachedSymbolNamesProvider( session, @@ -73,15 +77,15 @@ return getClassLikeSymbolByClassId(classId, null) } - /** - * [KotlinJavaDeclarationNamesProvider] provides *exact* package sets (including parent packages), so we can decide [hasPackage] without - * querying the Java facade. - */ override fun hasPackage(fqName: FqName): Boolean { - val packageNames = symbolNamesProvider.getPackageNames() - ?: return super.hasPackage(fqName) + val packageNames = packageNamesWithParentPackages - return fqName.asString() in packageNames + return if (packageNames != null) { + // The package set from Java symbol providers is *exact* if it can be computed, so we can decide `hasPackage` directly. + fqName.asString() in packageNames + } else { + super.hasPackage(fqName) + } } @LLModuleSpecificSymbolProviderAccess
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/combined/LLCombinedJavaSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/combined/LLCombinedJavaSymbolProvider.kt index e7da78b..22d3ab2 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/combined/LLCombinedJavaSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/combined/LLCombinedJavaSymbolProvider.kt
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.analysis.api.platform.caches.withStatsCounter import org.jetbrains.kotlin.analysis.low.level.api.fir.statistics.LLStatisticsService import org.jetbrains.kotlin.analysis.low.level.api.fir.symbolProviders.LLFirJavaSymbolProvider +import org.jetbrains.kotlin.analysis.low.level.api.fir.symbolProviders.computePackageNamesWithParentPackages import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.java.JavaSymbolProvider import org.jetbrains.kotlin.fir.java.hasMetadataAnnotation @@ -58,6 +59,10 @@ .withStatsCounter(LLStatisticsService.getInstance(project)?.symbolProviders?.combinedSymbolProviderClassCacheStatsCounter) } + private val packageNamesWithParentPackages by lazy(LazyThreadSafetyMode.PUBLICATION) { + symbolNamesProvider.getPackageNames()?.let { computePackageNamesWithParentPackages(it) } + } + override val symbolNamesProvider: FirSymbolNamesProvider = FirCompositeCachedSymbolNamesProvider.fromSymbolProviders(session, providers) override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { @@ -95,11 +100,14 @@ } override fun hasPackage(fqName: FqName): Boolean { - // The package set from Java symbol providers is *exact* if it can be computed. - val packageNames = symbolNamesProvider.getPackageNames() - ?: return providers.any { it.hasPackage(fqName) } + val packageNames = packageNamesWithParentPackages - return fqName.asString() in packageNames + return if (packageNames != null) { + // The package set from Java symbol providers is *exact* if it can be computed, so we can decide `hasPackage` directly. + fqName.asString() in packageNames + } else { + providers.any { it.hasPackage(fqName) } + } } override fun estimateSymbolCacheSize(): Long = classCache.estimatedSize
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/utils.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/utils.kt index 8db8c83..7b40570 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/utils.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/symbolProviders/utils.kt
@@ -28,6 +28,22 @@ internal fun String.isKotlinPackage(): Boolean = startsWith(KOTLIN_PACKAGE_PREFIX) +internal fun computePackageNamesWithParentPackages(packageNames: Set<String>): Set<String> { + if (packageNames.isEmpty()) return emptySet() + + return buildSet { + add("") + + packageNames.forEach { packageName -> + var currentPackage = packageName + while (currentPackage.isNotEmpty()) { + add(currentPackage) + currentPackage = currentPackage.substringBeforeLast(".", missingDelimiterValue = "") + } + } + } +} + /** * Checks if this [FirBasedSymbol] has the given PSI element as a source. *