[LL API] Fallback for designation path calculation for special cases

Currently, designation path requires all intermediate classes to be
regular named classes. This is not certainly possible, especially for
invalid cases such as:

---
class Foo {
    object {
        class Bar {
            fun foo() {} // What's the designation path for 'foo()'?
        }
    }
}
---

If there was a way to get a parent for a given FIR element, we could
just follow parents until the root FIR node (which would be a FirFile).
This commit is contained in:
Yan Zhulanow
2023-01-20 18:01:46 +09:00
committed by Space Team
parent e390d2aae3
commit 84baee1ef1
@@ -5,7 +5,11 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.api package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import org.jetbrains.kotlin.KtRealPsiSourceElement
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.nullableJavaSymbolProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.nullableJavaSymbolProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirResolvableSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirElementFinder import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirElementFinder
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getContainingFile import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getContainingFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirEntry import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirEntry
@@ -21,7 +25,10 @@ import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.diagnostics.ConeDestructuringDeclarationsOnTopLevel import org.jetbrains.kotlin.fir.diagnostics.ConeDestructuringDeclarationsOnTopLevel
import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.parents
class FirDesignationWithFile( class FirDesignationWithFile(
path: List<FirRegularClass>, path: List<FirRegularClass>,
@@ -124,8 +131,52 @@ private fun collectDesignationPathWithContainingClass(target: FirDeclaration, co
return declaration return declaration
} }
val chain = generateSequence(containingClassId) { it.outerClassId }.map { resolveChunk(it) } val chunks = generateSequence(containingClassId) { it.outerClassId }.toList()
return chain.toMutableList().also { it.reverse() }
if (chunks.any { it.shortClassName.isSpecial }) {
val fallbackResult = collectDesignationPathWithTreeTraversal(target)
if (fallbackResult != null) {
return fallbackResult
}
}
return chunks
.dropWhile { it.shortClassName.isSpecial }
.map { resolveChunk(it) }
.asReversed()
}
/*
This implementation is certainly inefficient, however there seem to be no better way to implement designation collection for
anonymous outer classes unless FIR tree gets a way to get an element parent.
*/
private fun collectDesignationPathWithTreeTraversal(target: FirDeclaration): List<FirRegularClass>? {
val containingFile = target.getContainingFile() ?: return null
val path = ArrayDeque<FirElement>()
path.addLast(containingFile)
var result: List<FirRegularClass>? = null
val visitor = object : FirVisitorVoid() {
override fun visitElement(element: FirElement) {
if (result != null) {
return
} else if (element === target) {
result = path.filterIsInstance<FirRegularClass>()
} else {
try {
path.addLast(element)
element.acceptChildren(this)
} finally {
path.removeLast()
}
}
}
}
containingFile.accept(visitor)
return result
} }
private fun getTargetSession(target: FirDeclaration): FirSession { private fun getTargetSession(target: FirDeclaration): FirSession {