From 34a1569375eeda424c8021f714de424d846c88ca Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Thu, 15 Dec 2022 15:47:23 +0100 Subject: [PATCH] [LL FIR] KT-55339 Fix supertype collection in LL FIR supertype resolver - LL FIR didn't report a `RECURSIVE_TYPEALIAS_EXPANSION` error on `F2` in `recursive.fir.kt`. While the resolver correctly associates `F2` with a cyclic error supertype in the supertype computation session, that error supertype was never assigned to `F2`. - Why? `F2`'s supertype is a function type, so the supertype's `classLikeDeclaration` in `collect` is `Function1`, which has no containing file. Thus, the `Function1` was simply skipped, without adding `F2` to the designators to which supertypes are later applied via `apply`. - The fix takes supertypes without containing files into account by crawling their type arguments, which possibly leads the collector to further designations, such as `F2`, which is reachable via the function type's type arguments. ^KT-55339 fixed --- ...rDesignatedSupertypeResolverTransformer.kt | 87 ++++++++++++------- .../tests/typealias/recursive.fir.kt | 1 - .../diagnostics/tests/typealias/recursive.kt | 1 - 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirDesignatedSupertypeResolverTransformer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirDesignatedSupertypeResolverTransformer.kt index ef56e4ed614..e336f99d15f 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirDesignatedSupertypeResolverTransformer.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirDesignatedSupertypeResolverTransformer.kt @@ -24,10 +24,13 @@ import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.toSymbol +import org.jetbrains.kotlin.fir.types.type /** - * Transform designation into SUPER_TYPES phase. Affects only for designation, target declaration, it's children and dependents + * Transform designation into SUPER_TYPES phase. Affects only the designation, target declaration, its children, and dependents. */ internal class LLFirDesignatedSupertypeResolverTransformer( private val designation: FirDesignationWithFile, @@ -70,41 +73,67 @@ internal class LLFirDesignatedSupertypeResolverTransformer( } } - private fun collect(designation: FirDesignationWithFile): Collection { - val visited = mutableMapOf() - val toVisit = mutableListOf() + private inner class DesignationCollector { + private val visited = mutableMapOf() + private val toVisit = mutableListOf() - toVisit.add(designation) - while (toVisit.isNotEmpty()) { - for (nowVisit in toVisit) { - checkCanceled() - val resolver = DesignatedFirSupertypeResolverVisitor(nowVisit) - nowVisit.firFile.lazyResolveToPhase(FirResolvePhase.IMPORTS) - lockProvider.runCustomResolveUnderLock(nowVisit.firFile) { - nowVisit.firFile.accept(resolver, null) + fun collect(designation: FirDesignationWithFile): Collection { + toVisit.add(designation) + while (toVisit.isNotEmpty()) { + toVisit.forEach(::visitDesignation) + toVisit.clear() + + // We want to apply resolved supertypes to as many designations as possible. So we crawl the resolved supertypes of visited + // designations to find more designations to collect. + for (supertypeComputationStatus in supertypeComputationSession.supertypeStatusMap.values) { + if (supertypeComputationStatus !is SupertypeComputationStatus.Computed) continue + supertypeComputationStatus.supertypeRefs.forEach { crawlSupertype(it.type) } } - resolver.declarationTransformer.ensureDesignationPassed() - visited[nowVisit.target] = nowVisit } - toVisit.clear() + return visited.values + } - for (value in supertypeComputationSession.supertypeStatusMap.values) { - if (value !is SupertypeComputationStatus.Computed) continue - for (reference in value.supertypeRefs) { - val classLikeDeclaration = reference.type.toSymbol(session)?.fir - if (classLikeDeclaration !is FirClassLikeDeclaration) continue - if (classLikeDeclaration is FirJavaClass) continue - if (visited.containsKey(classLikeDeclaration)) continue - val cache = classLikeDeclaration.llFirResolvableSession?.moduleComponents?.cache ?: continue - val containingFile = cache.getContainerFirFile(classLikeDeclaration) ?: continue - toVisit.add(classLikeDeclaration.collectDesignation(containingFile)) - } + private fun visitDesignation(designation: FirDesignationWithFile) { + checkCanceled() + val resolver = DesignatedFirSupertypeResolverVisitor(designation) + designation.firFile.lazyResolveToPhase(FirResolvePhase.IMPORTS) + lockProvider.runCustomResolveUnderLock(designation.firFile) { + designation.firFile.accept(resolver, null) + } + resolver.declarationTransformer.ensureDesignationPassed() + visited[designation.target] = designation + } + + private fun crawlSupertype(type: ConeKotlinType) { + val classLikeDeclaration = type.toSymbol(session)?.fir + if (classLikeDeclaration !is FirClassLikeDeclaration) return + if (classLikeDeclaration is FirJavaClass) return + if (visited.containsKey(classLikeDeclaration)) return + + val containingFile = + classLikeDeclaration.llFirResolvableSession?.moduleComponents?.cache?.getContainerFirFile(classLikeDeclaration) + if (containingFile != null) { + toVisit.add(classLikeDeclaration.collectDesignation(containingFile)) + } else if (type is ConeClassLikeType) { + // The `classLikeDeclaration` is not associated with a file and thus there is no need to resolve it, but it may still point + // to declarations via its type arguments which need to be collected and have a containing file. For example, a `Function1` + // could point to a type alias. + type.typeArguments.forEach { it.type?.let(::crawlSupertype) } } } - return visited.values } - private fun apply(visited: Collection) { + /** + * Resolves the supertypes of [designation] and collects all designations that resolved supertypes should be applied to, including + * [designation] itself and any designations discovered via resolved supertypes. + */ + private fun collect(designation: FirDesignationWithFile): Collection = + DesignationCollector().collect(designation) + + /** + * Applies the resolved supertypes in [supertypeComputationSession] to each designation in [designations]. + */ + private fun apply(designations: Collection) { fun applyToFileSymbols(designations: List) { for (designation in designations) { checkCanceled() @@ -114,7 +143,7 @@ internal class LLFirDesignatedSupertypeResolverTransformer( } } - val filesToDesignations = visited.groupBy { it.firFile } + val filesToDesignations = designations.groupBy { it.firFile } for (designationsPerFile in filesToDesignations) { checkCanceled() lockProvider.runCustomResolveUnderLock(designationsPerFile.key) { diff --git a/compiler/testData/diagnostics/tests/typealias/recursive.fir.kt b/compiler/testData/diagnostics/tests/typealias/recursive.fir.kt index f417250e8d5..a4c437cf15c 100644 --- a/compiler/testData/diagnostics/tests/typealias/recursive.fir.kt +++ b/compiler/testData/diagnostics/tests/typealias/recursive.fir.kt @@ -1,4 +1,3 @@ -// FIR_IDE_IGNORE typealias R = R typealias L = List diff --git a/compiler/testData/diagnostics/tests/typealias/recursive.kt b/compiler/testData/diagnostics/tests/typealias/recursive.kt index abab7c911b4..fcb1651ed59 100644 --- a/compiler/testData/diagnostics/tests/typealias/recursive.kt +++ b/compiler/testData/diagnostics/tests/typealias/recursive.kt @@ -1,4 +1,3 @@ -// FIR_IDE_IGNORE typealias R = R typealias L = List