FIR IDE: fix lazy resolve for local declarations

This commit is contained in:
Ilya Kirillov
2020-08-24 18:09:27 +03:00
parent 9d237033ad
commit 8363c711e6
2 changed files with 53 additions and 13 deletions
@@ -5,9 +5,12 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder
import com.intellij.psi.util.parentOfType
import com.intellij.psi.util.parentsOfType
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ResolutionMode
@@ -24,6 +27,8 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.ThreadSafe import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.ThreadSafe
import org.jetbrains.kotlin.idea.fir.low.level.api.util.getContainingFile import org.jetbrains.kotlin.idea.fir.low.level.api.util.getContainingFile
import org.jetbrains.kotlin.idea.util.classIdIfNonLocal
import org.jetbrains.kotlin.psi.psiUtil.parents
/** /**
* Maps [KtElement] to [FirElement] * Maps [KtElement] to [FirElement]
@@ -86,24 +91,27 @@ internal class FirElementBuilder(
firFileBuilder.runResolveWithoutLock(containerFirFile, fromPhase = firDeclarationToResolve.resolvePhase, toPhase = nonLazyPhase) firFileBuilder.runResolveWithoutLock(containerFirFile, fromPhase = firDeclarationToResolve.resolvePhase, toPhase = nonLazyPhase)
} }
if (toPhase <= nonLazyPhase) return if (toPhase <= nonLazyPhase) return
val designation = mutableListOf<FirDeclaration>(containerFirFile)
if (firDeclarationToResolve !is FirFile) {
val id = when (firDeclarationToResolve) { val nonLocalDeclarationToResolve = firDeclarationToResolve.getNonLocalDeclarationToResolve(provider)
val designation = mutableListOf<FirDeclaration>(containerFirFile)
if (nonLocalDeclarationToResolve !is FirFile) {
val id = when (nonLocalDeclarationToResolve) {
is FirCallableDeclaration<*> -> { is FirCallableDeclaration<*> -> {
firDeclarationToResolve.symbol.callableId.classId nonLocalDeclarationToResolve.symbol.callableId.classId
} }
is FirRegularClass -> { is FirRegularClass -> {
firDeclarationToResolve.symbol.classId nonLocalDeclarationToResolve.symbol.classId
} }
else -> error("Unsupported: ${firDeclarationToResolve.render()}") else -> error("Unsupported: ${nonLocalDeclarationToResolve.render()}")
} }
val outerClasses = generateSequence(id) { classId -> val outerClasses = generateSequence(id) { classId ->
classId.outerClassId classId.outerClassId
}.mapTo(mutableListOf()) { provider.getFirClassifierByFqName(it)!! } }.mapTo(mutableListOf()) { provider.getFirClassifierByFqName(it)!! }
designation += outerClasses.asReversed() designation += outerClasses.asReversed()
if (firDeclarationToResolve is FirCallableDeclaration<*>) { if (nonLocalDeclarationToResolve is FirCallableDeclaration<*>) {
designation += firDeclarationToResolve designation += nonLocalDeclarationToResolve
} }
} }
if (designation.all { it.resolvePhase >= toPhase }) { if (designation.all { it.resolvePhase >= toPhase }) {
@@ -131,11 +139,33 @@ internal class FirElementBuilder(
} }
} }
private fun FirDeclaration.getNonLocalDeclarationToResolve(provider: FirProvider): FirDeclaration {
if (this is FirFile) return this
val ktDeclaration = psi as? KtDeclaration ?: error("FirDeclaration should have a PSI of type KtDeclaration")
if (!KtPsiUtil.isLocal(ktDeclaration)) return this
return when (val nonLocalPsi = ktDeclaration.getNonLocalContainingDeclarationWithFqName()) {
is KtClassOrObject -> provider.getFirClassifierByFqName(
nonLocalPsi.classIdIfNonLocal()
?: error("Container classId should not be null for non-local declaration")
) ?: error("Could not find class ${nonLocalPsi.classIdIfNonLocal()}")
is KtProperty, is KtNamedFunction -> {
val containerClass = nonLocalPsi.containingClassOrObject
?: error("Container class should not be null for non-local declaration")
val containerClassId = containerClass.classIdIfNonLocal()
?: error("Container classId should not be null for non-local declaration")
val containingFir = provider.getFirClassifierByFqName(containerClassId) as? FirRegularClass
?: error("Could not find class $containerClassId")
containingFir.declarations.first { it.psi === nonLocalPsi }
}
else -> error("Invalid container ${nonLocalPsi?.let { it::class } ?: "null"}")
}
}
private fun KtElement.getNonLocalContainingDeclarationWithFqName(): KtDeclaration? {
private fun KtElement.getNonLocalContainingDeclarationWithFqName(): KtNamedDeclaration? {
var container = parent var container = parent
while (container != null && container !is KtFile) { while (container != null && container !is KtFile) {
if (container is KtDeclaration if (container is KtNamedDeclaration
&& (container is KtClassOrObject || container is KtDeclarationWithBody) && (container is KtClassOrObject || container is KtDeclarationWithBody)
&& !KtPsiUtil.isLocal(container) && !KtPsiUtil.isLocal(container)
&& container.name != null && container.name != null
@@ -9,10 +9,11 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.util.parentOfType import com.intellij.psi.util.parentOfType
import com.intellij.psi.util.parentsOfType
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtPackageDirective import org.jetbrains.kotlin.psi.*
fun KtElement.getElementTextInContext(): String { fun KtElement.getElementTextInContext(): String {
val context = parentOfType<KtImportDirective>() val context = parentOfType<KtImportDirective>()
@@ -33,5 +34,14 @@ fun KtElement.getElementTextInContext(): String {
}) })
return builder.toString().trimIndent().trim() return builder.toString().trimIndent().trim()
} }
private const val ELEMENT_TAG = "ELEMENT" private const val ELEMENT_TAG = "ELEMENT"
fun KtClassOrObject.classIdIfNonLocal(): ClassId? {
if (KtPsiUtil.isLocal(this)) return null
val packageName = containingKtFile.packageFqName
val classesNames = parentsOfType<KtDeclaration>(withSelf = true).map { it.name }.toList()
if (classesNames.any { it == null }) return null
return ClassId(packageName, FqName(classesNames.joinToString(separator = ".")), /*local=*/false)
}