[LL FIR] calculate designation path by FirFile where it is possible

This will fix problems with declaration clash.
Otherwise, a designation path to the constructor of the second class
from the snippet
```
class A
class A
```
Will contain the first class instead of the second

Also, this is more performed than trying to find a segment of
a path by providers

^KT-58546 Fixed
This commit is contained in:
Dmitrii Gridin
2023-05-09 16:23:49 +02:00
committed by Space Team
parent ae88726fae
commit b3aa2dd60f
9 changed files with 1499 additions and 42 deletions
@@ -116,11 +116,30 @@ private fun collectDesignationPath(target: FirElementWithResolveState): List<Fir
}
}
private fun collectDesignationPathWithContainingClassByFirFile(
firFile: FirFile,
containingClassId: ClassId,
target: FirDeclaration,
): List<FirRegularClass>? = FirElementFinder.findClassPathToDeclaration(
firFile = firFile,
declarationContainerClassId = containingClassId,
targetMemberDeclaration = target,
)
private fun collectDesignationPathWithContainingClass(target: FirDeclaration, containingClassId: ClassId): List<FirRegularClass>? {
if (containingClassId.isLocal) {
return null
}
val firFile = target.getContainingFile()
if (firFile != null && firFile.packageFqName == containingClassId.packageFqName) {
// We should do fallback to the heavy implementation if something goes wrong.
// For example, we can't be able to find an on-air declaration by this way
collectDesignationPathWithContainingClassByFirFile(firFile, containingClassId, target)?.let {
return it
}
}
val useSiteSession = getTargetSession(target)
fun resolveChunk(classId: ClassId): FirRegularClass {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirSingleResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.asResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.tryCollectDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockProvider
@@ -26,7 +27,6 @@ import org.jetbrains.kotlin.fir.resolve.transformers.SupertypeComputationSession
import org.jetbrains.kotlin.fir.resolve.transformers.SupertypeComputationStatus
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.fir.resolve.transformers.platformSupertypeUpdater
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId
@@ -86,6 +86,12 @@ private class LLFirSuperTypeTargetResolver(
// We can get into this function during a loop calculation, so it is possible that the result for [outerClass]
// is not yet published, so we expect that this class was already visited or resolved
if (outerClass !in visitedElements) {
outerClass.asResolveTarget()?.let { resolveTarget ->
// It is possible in case of declaration collision,
// so we need this logic only to be sure that [outerClass] is resolved
resolveToSupertypePhase(resolveTarget)
}
LLFirSupertypeLazyResolver.checkIsResolved(outerClass)
}
}
@@ -181,6 +187,25 @@ private class LLFirSuperTypeTargetResolver(
}
}
private fun FirClassLikeDeclaration.asResolveTarget(): LLFirSingleResolveTarget? {
return takeIf { it.canHaveLoopInSupertypesHierarchy(session, forceSkipResolvedClasses = true) }
?.tryCollectDesignationWithFile()
?.asResolveTarget()
}
private fun resolveToSupertypePhase(target: LLFirSingleResolveTarget) {
LLFirSuperTypeTargetResolver(
target = target,
lockProvider = lockProvider,
session = session,
scopeSession = scopeSession,
supertypeComputationSession = supertypeComputationSession,
visitedElements = visitedElements,
).resolveDesignation()
LLFirLazyPhaseResolverByPhase.getByPhase(resolverPhase).checkIsResolved(target)
}
/**
* 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.
@@ -205,21 +230,9 @@ private class LLFirSuperTypeTargetResolver(
return
}
val resolveTarget = classLikeDeclaration.takeIf { it.canHaveLoopInSupertypesHierarchy(session, forceSkipResolvedClasses = true) }
?.tryCollectDesignationWithFile()
?.asResolveTarget()
val resolveTarget = classLikeDeclaration.asResolveTarget()
if (resolveTarget != null) {
LLFirSuperTypeTargetResolver(
target = resolveTarget,
lockProvider = lockProvider,
session = session,
scopeSession = scopeSession,
supertypeComputationSession = supertypeComputationSession,
visitedElements = visitedElements,
).resolveDesignation()
LLFirLazyPhaseResolverByPhase.getByPhase(resolverPhase).checkIsResolved(resolveTarget)
resolveToSupertypePhase(resolveTarget)
} 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.
@@ -126,13 +126,11 @@ private class LLFirTypeTargetResolver(
override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
when (target) {
is FirConstructor -> {
// TODO: should be uncommented after KT-58546
// error("Shoul be resolved during ${::resolveConstructors.name}")
error("Should be resolved during ${::resolveConstructors.name}")
}
is FirDanglingModifierList, is FirFileAnnotationsContainer, is FirCallableDeclaration, is FirTypeAlias, is FirScript -> {
target.accept(transformer, null)
}
is FirRegularClass -> {
resolveClassTypes(target)
}
@@ -15,7 +15,30 @@ import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.name.ClassId
object FirElementFinder {
fun findClassifierWithClassId(firFile: FirFile, classId: ClassId): FirClassLikeDeclaration? {
fun findClassifierWithClassId(
firFile: FirFile,
classId: ClassId,
): FirClassLikeDeclaration? = findPathToClassifierWithClassId(
firFile = firFile,
classId = classId,
expectedMemberDeclaration = null
)?.second
fun findClassPathToDeclaration(
firFile: FirFile,
declarationContainerClassId: ClassId,
targetMemberDeclaration: FirDeclaration,
): List<FirRegularClass>? = findPathToClassifierWithClassId(
firFile = firFile,
classId = declarationContainerClassId,
expectedMemberDeclaration = targetMemberDeclaration
)?.first
private fun findPathToClassifierWithClassId(
firFile: FirFile,
classId: ClassId,
expectedMemberDeclaration: FirDeclaration?,
): Pair<List<FirRegularClass>, FirClassLikeDeclaration>? {
requireWithAttachmentBuilder(!classId.isLocal, { "ClassId should not be local" }) {
withEntry("classId", classId) { it.asString() }
}
@@ -28,33 +51,56 @@ object FirElementFinder {
}
val classIdPathSegment = classId.relativeClassName.pathSegments()
val path = ArrayList<FirRegularClass>(classIdPathSegment.size)
var result: FirClassLikeDeclaration? = null
fun find(declarations: Iterable<FirDeclaration>, classIdPathIndex: Int) {
if (result != null) return
fun find(declarations: Iterable<FirDeclaration>, classIdPathIndex: Int): Boolean {
val currentClassSegment = classIdPathSegment[classIdPathIndex]
for (subDeclaration in declarations) {
if (subDeclaration is FirScript) {
val scriptDeclarations = subDeclaration.statements.asSequence().filterIsInstance<FirDeclaration>()
find(scriptDeclarations.asIterable(), classIdPathIndex)
if (find(scriptDeclarations.asIterable(), classIdPathIndex)) {
return true
}
continue
}
if (subDeclaration is FirClassLikeDeclaration && currentClassSegment == subDeclaration.symbol.name) {
if (classIdPathIndex == classIdPathSegment.lastIndex) {
result = subDeclaration
return
if (expectedMemberDeclaration == null ||
subDeclaration is FirRegularClass && expectedMemberDeclaration in subDeclaration.declarations
) {
if (subDeclaration is FirRegularClass) {
path += subDeclaration
}
result = subDeclaration
return true
}
continue
}
if (subDeclaration is FirRegularClass) {
find(subDeclaration.declarations, classIdPathIndex + 1)
path += subDeclaration
if (find(subDeclaration.declarations, classIdPathIndex + 1)) {
return true
}
path.removeLast()
}
}
}
return false
}
find(firFile.declarations, classIdPathIndex = 0)
return result
return result?.let {
(path as List<FirRegularClass>) to it
}
}
inline fun <reified E : FirElement> findElementIn(
@@ -0,0 +1,41 @@
// MEMBER_CLASS_FILTER: org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
sealed class A
class B : A()
interface C : A
interface D : C, A
class E : B, A()
sealed class P {
object H: P()
class J : P()
object T {
object V : P()
class M : P()
}
val p: P = object : P() {
}
val r = object : P() {
}
}
class K : P()
object B {
class <caret>I : P()
}
fun test() {
class L : P()
val a = object : P() {
}
}
File diff suppressed because it is too large Load Diff
@@ -65,13 +65,13 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
SUPER_TYPES:
FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
[ResolvedTo(RAW_FIR)] annotations container
public? final? [ResolvedTo(SUPER_TYPES)] class A : R|kotlin/Any| {
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public? [ResolvedTo(RAW_FIR)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|>
}
}
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public? final? [ResolvedTo(SUPER_TYPES)] class A : R|kotlin/Any| {
public? [ResolvedTo(SUPER_TYPES)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|>
}
@@ -81,13 +81,13 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
TYPES:
FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
[ResolvedTo(RAW_FIR)] annotations container
public? final? [ResolvedTo(TYPES)] class A : R|kotlin/Any| {
public? [ResolvedTo(TYPES)] [ContainingClassKey=A] constructor(): R|A| {
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public? [ResolvedTo(RAW_FIR)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|>
}
}
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public? final? [ResolvedTo(TYPES)] class A : R|kotlin/Any| {
public? [ResolvedTo(TYPES)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|>
}
@@ -97,13 +97,13 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
STATUS:
FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
[ResolvedTo(RAW_FIR)] annotations container
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| {
public [ResolvedTo(STATUS)] [ContainingClassKey=A] constructor(): R|A| {
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public? [ResolvedTo(RAW_FIR)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|>
}
}
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| {
public [ResolvedTo(STATUS)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|>
}
@@ -113,13 +113,13 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
EXPECT_ACTUAL_MATCHING:
FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
[ResolvedTo(RAW_FIR)] annotations container
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| {
public [ResolvedTo(STATUS)] [ContainingClassKey=A] constructor(): R|A| {
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public? [ResolvedTo(RAW_FIR)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|>
}
}
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| {
public [ResolvedTo(EXPECT_ACTUAL_MATCHING)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|>
}
@@ -135,7 +135,7 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
}
}
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| {
public [ResolvedTo(ARGUMENTS_OF_ANNOTATIONS)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|>
}
@@ -151,7 +151,7 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
}
}
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| {
public [ResolvedTo(CONTRACTS)] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>()
}
@@ -167,7 +167,7 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
}
}
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| {
public [ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>()
}
@@ -183,7 +183,7 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
}
}
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| {
public [ResolvedTo(ANNOTATIONS_ARGUMENTS_MAPPING)] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>()
}
@@ -199,7 +199,7 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
}
}
public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| {
public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>()
}
@@ -84,6 +84,12 @@ public class FirOutOfContentRootLazyDeclarationResolveTestGenerated extends Abst
runTest("analysis/low-level-api-fir/testdata/lazyResolve/complexLocalHierarchy.kt");
}
@Test
@TestMetadata("complexRedeclaration.kt")
public void testComplexRedeclaration() throws Exception {
runTest("analysis/low-level-api-fir/testdata/lazyResolve/complexRedeclaration.kt");
}
@Test
@TestMetadata("cyclicHierarchy.kt")
public void testCyclicHierarchy() throws Exception {
@@ -84,6 +84,12 @@ public class FirSourceLazyDeclarationResolveTestGenerated extends AbstractFirSou
runTest("analysis/low-level-api-fir/testdata/lazyResolve/complexLocalHierarchy.kt");
}
@Test
@TestMetadata("complexRedeclaration.kt")
public void testComplexRedeclaration() throws Exception {
runTest("analysis/low-level-api-fir/testdata/lazyResolve/complexRedeclaration.kt");
}
@Test
@TestMetadata("cyclicHierarchy.kt")
public void testCyclicHierarchy() throws Exception {