[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>? { private fun collectDesignationPathWithContainingClass(target: FirDeclaration, containingClassId: ClassId): List<FirRegularClass>? {
if (containingClassId.isLocal) { if (containingClassId.isLocal) {
return null 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) val useSiteSession = getTargetSession(target)
fun resolveChunk(classId: ClassId): FirRegularClass { fun resolveChunk(classId: ClassId): FirRegularClass {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers 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.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.targets.asResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.tryCollectDesignationWithFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.tryCollectDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockProvider 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.SupertypeComputationStatus
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.fir.resolve.transformers.platformSupertypeUpdater 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.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId 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] // 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 // is not yet published, so we expect that this class was already visited or resolved
if (outerClass !in visitedElements) { 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) 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. * 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. * So we crawl the resolved supertypes of visited designations to find more designations to collect.
@@ -205,21 +230,9 @@ private class LLFirSuperTypeTargetResolver(
return return
} }
val resolveTarget = classLikeDeclaration.takeIf { it.canHaveLoopInSupertypesHierarchy(session, forceSkipResolvedClasses = true) } val resolveTarget = classLikeDeclaration.asResolveTarget()
?.tryCollectDesignationWithFile()
?.asResolveTarget()
if (resolveTarget != null) { if (resolveTarget != null) {
LLFirSuperTypeTargetResolver( resolveToSupertypePhase(resolveTarget)
target = resolveTarget,
lockProvider = lockProvider,
session = session,
scopeSession = scopeSession,
supertypeComputationSession = supertypeComputationSession,
visitedElements = visitedElements,
).resolveDesignation()
LLFirLazyPhaseResolverByPhase.getByPhase(resolverPhase).checkIsResolved(resolveTarget)
} else if (type is ConeClassLikeType) { } 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 // 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. // 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) { override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
when (target) { when (target) {
is FirConstructor -> { is FirConstructor -> {
// TODO: should be uncommented after KT-58546 error("Should be resolved during ${::resolveConstructors.name}")
// error("Shoul be resolved during ${::resolveConstructors.name}")
} }
is FirDanglingModifierList, is FirFileAnnotationsContainer, is FirCallableDeclaration, is FirTypeAlias, is FirScript -> { is FirDanglingModifierList, is FirFileAnnotationsContainer, is FirCallableDeclaration, is FirTypeAlias, is FirScript -> {
target.accept(transformer, null) target.accept(transformer, null)
} }
is FirRegularClass -> { is FirRegularClass -> {
resolveClassTypes(target) resolveClassTypes(target)
} }
@@ -15,7 +15,30 @@ import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
object FirElementFinder { 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" }) { requireWithAttachmentBuilder(!classId.isLocal, { "ClassId should not be local" }) {
withEntry("classId", classId) { it.asString() } withEntry("classId", classId) { it.asString() }
} }
@@ -28,33 +51,56 @@ object FirElementFinder {
} }
val classIdPathSegment = classId.relativeClassName.pathSegments() val classIdPathSegment = classId.relativeClassName.pathSegments()
val path = ArrayList<FirRegularClass>(classIdPathSegment.size)
var result: FirClassLikeDeclaration? = null var result: FirClassLikeDeclaration? = null
fun find(declarations: Iterable<FirDeclaration>, classIdPathIndex: Int) { fun find(declarations: Iterable<FirDeclaration>, classIdPathIndex: Int): Boolean {
if (result != null) return
val currentClassSegment = classIdPathSegment[classIdPathIndex] val currentClassSegment = classIdPathSegment[classIdPathIndex]
for (subDeclaration in declarations) { for (subDeclaration in declarations) {
if (subDeclaration is FirScript) { if (subDeclaration is FirScript) {
val scriptDeclarations = subDeclaration.statements.asSequence().filterIsInstance<FirDeclaration>() val scriptDeclarations = subDeclaration.statements.asSequence().filterIsInstance<FirDeclaration>()
find(scriptDeclarations.asIterable(), classIdPathIndex) if (find(scriptDeclarations.asIterable(), classIdPathIndex)) {
return true
}
continue continue
} }
if (subDeclaration is FirClassLikeDeclaration && currentClassSegment == subDeclaration.symbol.name) { if (subDeclaration is FirClassLikeDeclaration && currentClassSegment == subDeclaration.symbol.name) {
if (classIdPathIndex == classIdPathSegment.lastIndex) { if (classIdPathIndex == classIdPathSegment.lastIndex) {
result = subDeclaration if (expectedMemberDeclaration == null ||
return subDeclaration is FirRegularClass && expectedMemberDeclaration in subDeclaration.declarations
) {
if (subDeclaration is FirRegularClass) {
path += subDeclaration
}
result = subDeclaration
return true
}
continue
} }
if (subDeclaration is FirRegularClass) { 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) find(firFile.declarations, classIdPathIndex = 0)
return result return result?.let {
(path as List<FirRegularClass>) to it
}
} }
inline fun <reified E : FirElement> findElementIn( 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: SUPER_TYPES:
FILE: [ResolvedTo(IMPORTS)] redeclaration.kt FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
[ResolvedTo(RAW_FIR)] annotations container [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| { public? [ResolvedTo(RAW_FIR)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|> 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| { public? [ResolvedTo(SUPER_TYPES)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|> LAZY_super<R|kotlin/Any|>
} }
@@ -81,13 +81,13 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
TYPES: TYPES:
FILE: [ResolvedTo(IMPORTS)] redeclaration.kt FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
[ResolvedTo(RAW_FIR)] annotations container [ResolvedTo(RAW_FIR)] annotations container
public? final? [ResolvedTo(TYPES)] class A : R|kotlin/Any| { public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public? [ResolvedTo(TYPES)] [ContainingClassKey=A] constructor(): R|A| { public? [ResolvedTo(RAW_FIR)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|> 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| { public? [ResolvedTo(TYPES)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|> LAZY_super<R|kotlin/Any|>
} }
@@ -97,13 +97,13 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
STATUS: STATUS:
FILE: [ResolvedTo(IMPORTS)] redeclaration.kt FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
[ResolvedTo(RAW_FIR)] annotations container [ResolvedTo(RAW_FIR)] annotations container
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| { public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public [ResolvedTo(STATUS)] [ContainingClassKey=A] constructor(): R|A| { public? [ResolvedTo(RAW_FIR)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|> 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| { public [ResolvedTo(STATUS)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|> LAZY_super<R|kotlin/Any|>
} }
@@ -113,13 +113,13 @@ FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
EXPECT_ACTUAL_MATCHING: EXPECT_ACTUAL_MATCHING:
FILE: [ResolvedTo(IMPORTS)] redeclaration.kt FILE: [ResolvedTo(IMPORTS)] redeclaration.kt
[ResolvedTo(RAW_FIR)] annotations container [ResolvedTo(RAW_FIR)] annotations container
public final [ResolvedTo(STATUS)] class A : R|kotlin/Any| { public? final? [ResolvedTo(RAW_FIR)] class A : R|kotlin/Any| {
public [ResolvedTo(STATUS)] [ContainingClassKey=A] constructor(): R|A| { public? [ResolvedTo(RAW_FIR)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|> 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| { public [ResolvedTo(EXPECT_ACTUAL_MATCHING)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|> 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| { public [ResolvedTo(ARGUMENTS_OF_ANNOTATIONS)] [ContainingClassKey=A] constructor(): R|A| {
LAZY_super<R|kotlin/Any|> 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| { public [ResolvedTo(CONTRACTS)] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>() 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| { public [ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>() 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| { public [ResolvedTo(ANNOTATIONS_ARGUMENTS_MAPPING)] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>() 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| { public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>() super<R|kotlin/Any|>()
} }
@@ -84,6 +84,12 @@ public class FirOutOfContentRootLazyDeclarationResolveTestGenerated extends Abst
runTest("analysis/low-level-api-fir/testdata/lazyResolve/complexLocalHierarchy.kt"); 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 @Test
@TestMetadata("cyclicHierarchy.kt") @TestMetadata("cyclicHierarchy.kt")
public void testCyclicHierarchy() throws Exception { public void testCyclicHierarchy() throws Exception {
@@ -84,6 +84,12 @@ public class FirSourceLazyDeclarationResolveTestGenerated extends AbstractFirSou
runTest("analysis/low-level-api-fir/testdata/lazyResolve/complexLocalHierarchy.kt"); 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 @Test
@TestMetadata("cyclicHierarchy.kt") @TestMetadata("cyclicHierarchy.kt")
public void testCyclicHierarchy() throws Exception { public void testCyclicHierarchy() throws Exception {