FIR IDE: do not fail on invalid code

This commit is contained in:
Ilya Kirillov
2020-10-20 17:04:04 +03:00
parent c646cb3d7e
commit 7dcda00e1d
5 changed files with 72 additions and 32 deletions
@@ -73,6 +73,7 @@ fun KtElement.getNonLocalContainingOrThisDeclaration(): KtNamedDeclaration? {
while (container != null && container !is KtFile) { while (container != null && container !is KtFile) {
if (container is KtNamedDeclaration if (container is KtNamedDeclaration
&& (container is KtClassOrObject || container is KtDeclarationWithBody || container is KtProperty || container is KtTypeAlias) && (container is KtClassOrObject || container is KtDeclarationWithBody || container is KtProperty || container is KtTypeAlias)
&& container !is KtPrimaryConstructor
&& !KtPsiUtil.isLocal(container) && !KtPsiUtil.isLocal(container)
&& container !is KtEnumEntry && container !is KtEnumEntry
&& container.containingClassOrObject !is KtEnumEntry && container.containingClassOrObject !is KtEnumEntry
@@ -5,26 +5,25 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve package org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve
import org.jetbrains.kotlin.fir.containingClass import com.intellij.psi.util.parentsOfType
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
import org.jetbrains.kotlin.idea.fir.low.level.api.trasformers.FirDesignatedBodyResolveTransformerForIDE
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingOrThisDeclaration import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.trasformers.FirDesignatedBodyResolveTransformerForIDE
import org.jetbrains.kotlin.idea.fir.low.level.api.trasformers.FirDesignatedContractsResolveTransformerForIDE import org.jetbrains.kotlin.idea.fir.low.level.api.trasformers.FirDesignatedContractsResolveTransformerForIDE
import org.jetbrains.kotlin.idea.fir.low.level.api.trasformers.FirDesignatedImplicitTypesTransformerForIDE import org.jetbrains.kotlin.idea.fir.low.level.api.trasformers.FirDesignatedImplicitTypesTransformerForIDE
import org.jetbrains.kotlin.idea.fir.low.level.api.util.*
import org.jetbrains.kotlin.idea.fir.low.level.api.util.checkCanceled import org.jetbrains.kotlin.idea.fir.low.level.api.util.checkCanceled
import org.jetbrains.kotlin.idea.fir.low.level.api.util.executeWithoutPCE import org.jetbrains.kotlin.idea.fir.low.level.api.util.executeWithoutPCE
import org.jetbrains.kotlin.idea.fir.low.level.api.util.findSourceNonLocalFirDeclaration import org.jetbrains.kotlin.idea.fir.low.level.api.util.findSourceNonLocalFirDeclaration
import org.jetbrains.kotlin.idea.fir.low.level.api.util.getContainingFile import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtPsiUtil
internal class FirLazyDeclarationResolver( internal class FirLazyDeclarationResolver(
private val firFileBuilder: FirFileBuilder private val firFileBuilder: FirFileBuilder
@@ -131,20 +130,14 @@ internal class FirLazyDeclarationResolver(
val nonLocalDeclarationToResolve = firDeclarationToResolve.getNonLocalDeclarationToResolve(provider, moduleFileCache) val nonLocalDeclarationToResolve = firDeclarationToResolve.getNonLocalDeclarationToResolve(provider, moduleFileCache)
val designation = mutableListOf<FirDeclaration>(containerFirFile) val designation = mutableListOf<FirDeclaration>(containerFirFile)
if (nonLocalDeclarationToResolve !is FirFile) { if (nonLocalDeclarationToResolve !is FirFile) {
val id = when (nonLocalDeclarationToResolve) { val ktDeclaration = firDeclarationToResolve.ktDeclaration
is FirCallableDeclaration<*> -> { designation += ktDeclaration.parentsOfType<KtClassOrObject>()
nonLocalDeclarationToResolve.containingClass()?.classId .filter { it !is KtEnumEntry }
} .map { it.findSourceNonLocalFirDeclaration(firFileBuilder, provider.symbolProvider, moduleFileCache) }
is FirClassLikeDeclaration<*> -> { .toList()
nonLocalDeclarationToResolve.symbol.classId .asReversed()
}
else -> error("Unsupported: ${nonLocalDeclarationToResolve.render()}")
}
val outerClasses = generateSequence(id) { classId ->
classId.outerClassId
}.mapTo(mutableListOf()) { provider.getFirClassifierByFqName(it)!! }
designation += outerClasses.asReversed()
if (nonLocalDeclarationToResolve is FirCallableDeclaration<*>) { if (nonLocalDeclarationToResolve is FirCallableDeclaration<*>) {
designation += nonLocalDeclarationToResolve designation += nonLocalDeclarationToResolve
} }
@@ -6,20 +6,31 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.util package org.jetbrains.kotlin.idea.fir.low.level.api.util
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
object FirElementFinder { object FirElementFinder {
inline fun <reified E : FirElement> findElementIn(container: FirElement, crossinline predicate: (E) -> Boolean): E? { inline fun <reified E : FirElement> findElementIn(
container: FirElement,
crossinline goInside: (E) -> Boolean = { true },
crossinline predicate: (E) -> Boolean,
): E? {
var result: E? = null var result: E? = null
container.accept(object : FirVisitorVoid() { container.accept(object : FirVisitorVoid() {
override fun visitElement(element: FirElement) { override fun visitElement(element: FirElement) {
if (result != null) return if (result != null) return
if (element is E && predicate(element)) { when {
result = element element !is E || element is FirFile -> {
} else { element.acceptChildren(this)
element.acceptChildren(this) }
predicate(element) -> {
result = element
}
goInside(element) -> {
element.acceptChildren(this)
}
} }
} }
}) })
@@ -9,10 +9,12 @@ import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.realPsi
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
import org.jetbrains.kotlin.idea.util.classIdIfNonLocal import org.jetbrains.kotlin.idea.util.classIdIfNonLocal
import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
@@ -22,30 +24,53 @@ internal fun KtDeclaration.findSourceNonLocalFirDeclaration(
firSymbolProvider: FirSymbolProvider, firSymbolProvider: FirSymbolProvider,
moduleFileCache: ModuleFileCache moduleFileCache: ModuleFileCache
): FirDeclaration { ): FirDeclaration {
require(!KtPsiUtil.isLocal(this)) //TODO test what way faster
return when { findSourceNonLocalFirDeclarationByProvider(firFileBuilder, firSymbolProvider, moduleFileCache)?.let { return it }
findSourceOfNonLocalFirDeclarationByTraversingWholeTree(firFileBuilder, moduleFileCache)?.let { return it }
error("No fir element was found for\n${getElementTextInContext()}")
}
private fun KtDeclaration.findSourceOfNonLocalFirDeclarationByTraversingWholeTree(
firFileBuilder: FirFileBuilder,
moduleFileCache: ModuleFileCache,
): FirDeclaration? {
val firFile = firFileBuilder.buildRawFirFileWithCaching(containingKtFile, moduleFileCache, lazyBodiesMode = true)
val originalDeclaration = originalDeclaration
return FirElementFinder.findElementIn(firFile, goInside = { it is FirRegularClass }) { firDeclaration ->
firDeclaration.psi == this || firDeclaration.psi == originalDeclaration
}
}
private fun KtDeclaration.findSourceNonLocalFirDeclarationByProvider(
firFileBuilder: FirFileBuilder,
firSymbolProvider: FirSymbolProvider,
moduleFileCache: ModuleFileCache
): FirDeclaration? {
val candidate = when {
this is KtClassOrObject -> findFir(firSymbolProvider) this is KtClassOrObject -> findFir(firSymbolProvider)
this is KtNamedDeclaration && (this is KtProperty || this is KtNamedFunction) -> { this is KtNamedDeclaration && (this is KtProperty || this is KtNamedFunction) -> {
val containerClass = containingClassOrObject val containerClass = containingClassOrObject
val declarations = if (containerClass != null) { val declarations = if (containerClass != null) {
val containerClassFir = containerClass.findFir(firSymbolProvider) val containerClassFir = containerClass.findFir(firSymbolProvider)
containerClassFir.declarations containerClassFir?.declarations
} else { } else {
val ktFile = containingKtFile val ktFile = containingKtFile
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache, lazyBodiesMode = true) val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache, lazyBodiesMode = true)
firFile.declarations firFile.declarations
} }
val original = originalDeclaration val original = originalDeclaration
declarations.first { it.psi === this || it.psi == original } declarations?.first { it.psi === this || it.psi == original }
} }
this is KtConstructor<*> -> { this is KtConstructor<*> -> {
val containerClassFir = containingClassOrObject?.findFir(firSymbolProvider) val containingClass = containingClassOrObject
?: error("Container class should be not null for KtConstructor") ?: error("Container class should be not null for KtConstructor")
val containerClassFir = containingClass.findFir(firSymbolProvider) ?: return null
containerClassFir.declarations.first { it.psi === this } containerClassFir.declarations.first { it.psi === this }
} }
this is KtTypeAlias -> findFir(firSymbolProvider) this is KtTypeAlias -> findFir(firSymbolProvider)
else -> error("Invalid container $this::class") else -> error("Invalid container $this::class")
} }
return candidate?.takeIf { it.realPsi == this }
} }
val ORIGINAL_DECLARATION_KEY = com.intellij.openapi.util.Key<KtDeclaration>("ORIGINAL_DECLARATION_KEY") val ORIGINAL_DECLARATION_KEY = com.intellij.openapi.util.Key<KtDeclaration>("ORIGINAL_DECLARATION_KEY")
@@ -53,9 +78,8 @@ val ORIGINAL_DECLARATION_KEY = com.intellij.openapi.util.Key<KtDeclaration>("ORI
var KtDeclaration.originalDeclaration by UserDataProperty(ORIGINAL_DECLARATION_KEY) var KtDeclaration.originalDeclaration by UserDataProperty(ORIGINAL_DECLARATION_KEY)
private fun KtClassOrObject.findFir(firSymbolProvider: FirSymbolProvider): FirRegularClass { private fun KtClassOrObject.findFir(firSymbolProvider: FirSymbolProvider): FirRegularClass? {
val classId = classIdIfNonLocal() val classId = classIdIfNonLocal() ?: return null
?: error("Container classId should not be null for non-local declaration")
return executeWithoutPCE { return executeWithoutPCE {
firSymbolProvider.getClassLikeSymbolByFqName(classId)?.fir as? FirRegularClass firSymbolProvider.getClassLikeSymbolByFqName(classId)?.fir as? FirRegularClass
?: error("Could not find class $classId") ?: error("Could not find class $classId")
@@ -8,8 +8,12 @@ package org.jetbrains.kotlin.idea.fir.low.level.api.util
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.ProgressManager
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.diagnostics.FirDiagnosticHolder import org.jetbrains.kotlin.fir.diagnostics.FirDiagnosticHolder
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.psi.KtDeclaration
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
@@ -52,6 +56,13 @@ internal inline fun checkCanceled() {
internal val FirElement.isErrorElement internal val FirElement.isErrorElement
get() = this is FirDiagnosticHolder get() = this is FirDiagnosticHolder
internal val FirDeclaration.ktDeclaration: KtDeclaration
get() {
val psi = psi
?: error("PSI element was not found for${render()}")
return psi as KtDeclaration
}
internal fun IdeaModuleInfo.collectTransitiveDependenciesWithSelf(): List<IdeaModuleInfo> { internal fun IdeaModuleInfo.collectTransitiveDependenciesWithSelf(): List<IdeaModuleInfo> {
val result = mutableSetOf<IdeaModuleInfo>() val result = mutableSetOf<IdeaModuleInfo>()