FIR IDE: move low level api main sources to the analysis directory

This commit is contained in:
Ilya Kirillov
2021-09-14 16:38:02 +02:00
parent 0c04ff98c3
commit b70f4f581e
112 changed files with 355 additions and 356 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ dependencies {
compileOnly(project(":compiler:frontend"))
compileOnly(project(":core:compiler.common"))
compileOnly(project(":core:compiler.common.jvm"))
compileOnly(project(":idea-frontend-fir:idea-fir-low-level-api"))
compileOnly(project(":analysis:low-level-api-fir"))
implementation(project(":analysis:analysis-api-providers"))
compile(intellijCoreDep()) { includeJars("intellij-core", "guava", rootProject = rootProject) }
@@ -0,0 +1,70 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(project(":compiler:psi"))
compile(project(":compiler:fir:fir2ir"))
compile(project(":compiler:fir:fir2ir:jvm-backend"))
compile(project(":compiler:ir.serialization.common"))
compile(project(":compiler:fir:resolve"))
compile(project(":compiler:fir:checkers"))
compile(project(":compiler:fir:checkers:checkers.jvm"))
compile(project(":compiler:fir:java"))
compile(project(":compiler:backend.common.jvm"))
testCompile(project(":idea-frontend-fir"))
implementation(project(":compiler:ir.psi2ir"))
implementation(project(":compiler:fir:entrypoint"))
implementation(project(":analysis:analysis-api-providers"))
compile(intellijCoreDep()) { includeJars("intellij-core", "guava", rootProject = rootProject) }
testCompile(projectTests(":compiler:test-infrastructure-utils"))
testCompile(projectTests(":compiler:test-infrastructure"))
testCompile(projectTests(":compiler:tests-common-new"))
testImplementation("org.opentest4j:opentest4j:1.2.0")
testCompile(toolsJar())
testCompile(projectTests(":compiler:tests-common"))
testCompile(projectTests(":compiler:fir:analysis-tests:legacy-fir-tests"))
testCompile(project(":kotlin-test:kotlin-test-junit"))
testApiJUnit5()
testCompile(project(":kotlin-reflect"))
testImplementation(project(":analysis:symbol-light-classes"))
testRuntimeOnly(intellijDep()) {
includeJars(
"jps-model",
"extensions",
"util",
"platform-api",
"platform-impl",
"idea",
"guava",
"trove4j",
"asm-all",
"log4j",
"jdom",
"streamex",
"bootstrap",
"jna",
rootProject = rootProject
)
}
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
projectTest(jUnit5Enabled = true) {
dependsOn(":dist")
workingDir = rootDir
useJUnitPlatform()
}
testsJar()
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignation
abstract class ContextByDesignationCollector<C : Any>(private val designation: FirDeclarationDesignation) {
private var context: C? = null
private val designationState = FirDesignationState(designation)
protected abstract fun getCurrentContext(): C
protected abstract fun goToNestedDeclaration(declaration: FirDeclaration)
fun getCollectedContext(): C {
return context
?: error("Context is not collected yet")
}
fun nextStep() {
if (designationState.canGoNext()) {
designationState.goNext()
if (designationState.currentDeclarationIfPresent == designation.declaration) {
check(context == null)
context = getCurrentContext()
}
goToNestedDeclaration(designationState.currentDeclaration)
} else {
if (designationState.currentDeclarationIfPresent == designation.declaration) {
designationState.goToInnerDeclaration()
}
}
}
}
private class FirDesignationState(val designation: FirDeclarationDesignation) {
/**
* Holds current declaration index
* if `currentIndex in [0, designation.path.lastIndex]` then current declaration is in path
* if `currentIndex == `designation.path.lastIndex + 1` then current declaration is our target declaration
* if `currentIndex > designation.path.lastIndex + 1` then we are inside current declaration
*/
private var currentIndex = -1
fun canGoNext(): Boolean = currentIndex < designation.path.size
val currentDeclarationIfPresent: FirDeclaration?
get() = when (currentIndex) {
in designation.path.indices -> designation.path[currentIndex]
designation.path.size -> designation.declaration
else -> null
}
val currentDeclaration: FirDeclaration
get() = currentDeclarationIfPresent
?: error("Went inside target declaration")
fun goNext() {
if (canGoNext()) {
currentIndex++
} else {
throw IndexOutOfBoundsException()
}
}
fun goToInnerDeclaration() {
if (currentIndex == designation.path.size) {
currentIndex++
} else {
throw IndexOutOfBoundsException()
}
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootModificationTracker
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.FirIdeSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.FirIdeSessionProviderStorage
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.*
import java.util.concurrent.ConcurrentHashMap
internal class FirIdeResolveStateService(project: Project) {
private val sessionProviderStorage = FirIdeSessionProviderStorage(project)
private val stateCache by cachedValue(
project,
project.createProjectWideOutOfBlockModificationTracker(),
ProjectRootModificationTracker.getInstance(project),
) {
ConcurrentHashMap<ModuleInfo, FirModuleResolveStateImpl>()
}
fun getResolveState(moduleInfo: ModuleInfo): FirModuleResolveStateImpl =
stateCache.computeIfAbsent(moduleInfo) { createResolveStateFor(moduleInfo, sessionProviderStorage) }
companion object {
fun getInstance(project: Project): FirIdeResolveStateService =
ServiceManager.getService(project, FirIdeResolveStateService::class.java)
internal fun createResolveStateFor(
moduleInfo: ModuleInfo,
sessionProviderStorage: FirIdeSessionProviderStorage,
configureSession: (FirIdeSession.() -> Unit)? = null,
): FirModuleResolveStateImpl {
if (moduleInfo !is ModuleSourceInfoBase) {
error("Creating FirModuleResolveState is not yet supported for $moduleInfo")
}
val sessionProvider = sessionProviderStorage.getSessionProvider(moduleInfo, configureSession)
val firFileBuilder = sessionProvider.rootModuleSession.firFileBuilder
return FirModuleResolveStateImpl(
sessionProviderStorage.project,
moduleInfo,
sessionProvider,
firFileBuilder,
FirLazyDeclarationResolver(firFileBuilder),
)
}
}
}
@TestOnly
fun createResolveStateForNoCaching(
moduleInfo: ModuleInfo,
project: Project,
configureSession: (FirIdeSession.() -> Unit)? = null,
): FirModuleResolveState =
FirIdeResolveStateService.createResolveStateFor(
moduleInfo = moduleInfo,
sessionProviderStorage = FirIdeSessionProviderStorage(project),
configureSession = configureSession
)
@@ -0,0 +1,7 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir
@@ -0,0 +1,7 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerContextProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.KtToFirMapping
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveType
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.containingKtFileIfAny
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalKtFile
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtLambdaExpression
internal class FirModuleResolveStateDepended(
private val originalState: FirModuleResolveStateImpl,
val towerProviderBuiltUponElement: FirTowerContextProvider,
private val ktToFirMapping: KtToFirMapping?,
) : FirModuleResolveState() {
override val project: Project get() = originalState.project
override val moduleInfo: ModuleInfo get() = originalState.moduleInfo
override val rootModuleSession get() = originalState.rootModuleSession
private val fileStructureCache get() = originalState.fileStructureCache
override fun getSessionFor(moduleInfo: ModuleInfo): FirSession =
originalState.getSessionFor(moduleInfo)
override fun getOrBuildFirFor(element: KtElement): FirElement? {
val elementBuilder = originalState.elementBuilder
val psi = elementBuilder.getPsiAsFirElementSource(element) ?: return null
if (!elementBuilder.doKtElementHasCorrespondingFirElement(element)) return null
ktToFirMapping?.getFirOfClosestParent(psi, this)?.let { return it }
return elementBuilder.getOrBuildFirFor(
element = element,
firFileBuilder = originalState.firFileBuilder,
moduleFileCache = originalState.rootModuleSession.cache,
fileStructureCache = fileStructureCache,
firLazyDeclarationResolver = originalState.firLazyDeclarationResolver,
state = this,
)
}
override fun getOrBuildFirFile(ktFile: KtFile): FirFile =
originalState.getOrBuildFirFile(ktFile)
override fun <D : FirDeclaration> resolveFirToPhase(declaration: D, toPhase: FirResolvePhase): D =
originalState.resolveFirToPhase(declaration, toPhase)
override fun <D : FirDeclaration> resolveFirToResolveType(declaration: D, type: ResolveType): D =
originalState.resolveFirToResolveType(declaration, type)
override fun tryGetCachedFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? {
val ktFile = declaration.containingKtFileIfAny ?: return null
cache.getCachedFirFile(ktFile)?.let { return it }
ktFile.originalKtFile?.let(cache::getCachedFirFile)?.let { return it }
return null
}
override fun getDiagnostics(element: KtElement, filter: DiagnosticCheckerFilter): List<FirPsiDiagnostic> =
TODO("Diagnostics are not implemented for depended state")
override fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic> =
TODO("Diagnostics are not implemented for depended state")
@OptIn(InternalForInline::class)
override fun findSourceFirDeclaration(ktDeclaration: KtLambdaExpression): FirDeclaration =
originalState.findSourceFirDeclaration(ktDeclaration)
@OptIn(InternalForInline::class)
override fun findSourceFirDeclaration(ktDeclaration: KtDeclaration): FirDeclaration =
originalState.findSourceFirDeclaration(ktDeclaration)
@OptIn(InternalForInline::class)
override fun findSourceFirCompiledDeclaration(ktDeclaration: KtDeclaration) =
originalState.findSourceFirDeclaration(ktDeclaration)
}
@@ -0,0 +1,168 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.providers.getModuleInfo
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.FirAnonymousFunctionExpression
import org.jetbrains.kotlin.fir.expressions.FirAnonymousObjectExpression
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.DiagnosticsCollector
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirElementBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.FileStructureCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveType
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.lazyResolveDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firIdeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.FirIdeSessionProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.FirIdeSourcesSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirDeclarationForCompiledElementSearcher
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.findSourceNonLocalFirDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getElementTextInContext
import org.jetbrains.kotlin.psi.*
internal class FirModuleResolveStateImpl(
override val project: Project,
override val moduleInfo: ModuleInfo,
private val sessionProvider: FirIdeSessionProvider,
val firFileBuilder: FirFileBuilder,
val firLazyDeclarationResolver: FirLazyDeclarationResolver,
) : FirModuleResolveState() {
override val rootModuleSession: FirIdeSourcesSession get() = sessionProvider.rootModuleSession
/**
* WARNING! This object contains scopes for all statements and declarations that were ever resolved.
* It can grow unbounded if you never edit the files in the opened project.
*
* It is a temporary solution until we can retrieve scopes for any fir element without re-resolving it.
*/
val fileStructureCache = FileStructureCache(firFileBuilder, firLazyDeclarationResolver)
val elementBuilder = FirElementBuilder()
private val diagnosticsCollector = DiagnosticsCollector(fileStructureCache, rootModuleSession.cache)
override fun getSessionFor(moduleInfo: ModuleInfo): FirSession =
sessionProvider.getSession(moduleInfo)!!
override fun getOrBuildFirFor(element: KtElement): FirElement? =
elementBuilder.getOrBuildFirFor(
element = element,
firFileBuilder = firFileBuilder,
moduleFileCache = rootModuleSession.cache,
fileStructureCache = fileStructureCache,
firLazyDeclarationResolver = firLazyDeclarationResolver,
state = this
)
override fun getOrBuildFirFile(ktFile: KtFile): FirFile =
firFileBuilder.buildRawFirFileWithCaching(ktFile, rootModuleSession.cache, preferLazyBodies = false)
override fun tryGetCachedFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? =
cache.getContainerFirFile(declaration)
override fun getDiagnostics(element: KtElement, filter: DiagnosticCheckerFilter): List<FirPsiDiagnostic> =
diagnosticsCollector.getDiagnosticsFor(element, filter)
override fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic> =
diagnosticsCollector.collectDiagnosticsForFile(ktFile, filter)
@OptIn(InternalForInline::class)
override fun findSourceFirDeclaration(ktDeclaration: KtDeclaration): FirDeclaration =
findSourceFirDeclarationByExpression(ktDeclaration)
@OptIn(InternalForInline::class)
override fun findSourceFirDeclaration(ktDeclaration: KtLambdaExpression): FirDeclaration =
findSourceFirDeclarationByExpression(ktDeclaration)
/**
* [ktDeclaration] should be either [KtDeclaration] or [KtLambdaExpression]
*/
private fun findSourceFirDeclarationByExpression(ktDeclaration: KtExpression): FirDeclaration {
val moduleInfo = ktDeclaration.getModuleInfo() as? ModuleSourceInfoBase
require(moduleInfo != null) {
"Declaration should have ModuleSourceInfo, instead it had ${ktDeclaration.getModuleInfo()}"
}
val nonLocalNamedDeclaration = ktDeclaration.getNonLocalContainingOrThisDeclaration()
?: error("Declaration should have non-local container${ktDeclaration.getElementTextInContext()}")
if (ktDeclaration == nonLocalNamedDeclaration) {
return nonLocalNamedDeclaration.findSourceNonLocalFirDeclaration(
firFileBuilder = firFileBuilder,
firSymbolProvider = rootModuleSession.firIdeProvider.symbolProvider,
moduleFileCache = sessionProvider.getModuleCache(moduleInfo)
)
}
return when (val localFirElement = getOrBuildFirFor(ktDeclaration)) {
is FirDeclaration -> localFirElement
is FirAnonymousFunctionExpression -> localFirElement.anonymousFunction
is FirAnonymousObjectExpression -> localFirElement.anonymousObject
else -> error("FirDeclaration was not found for\n${ktDeclaration.getElementTextInContext()}")
}
}
@OptIn(InternalForInline::class)
override fun findSourceFirCompiledDeclaration(ktDeclaration: KtDeclaration): FirDeclaration {
require(ktDeclaration.containingKtFile.isCompiled) {
"This method will only work on compiled declarations, but this declaration is not compiled: ${ktDeclaration.getElementTextInContext()}"
}
val searcher = FirDeclarationForCompiledElementSearcher(rootModuleSession.symbolProvider)
return when (ktDeclaration) {
is KtClassOrObject -> searcher.findNonLocalClass(ktDeclaration)
is KtConstructor<*> -> searcher.findConstructorOfNonLocalClass(ktDeclaration)
is KtNamedFunction -> searcher.findNonLocalFunction(ktDeclaration)
is KtProperty -> searcher.findNonLocalProperty(ktDeclaration)
else -> error("Unsupported compiled declaration of type ${ktDeclaration::class}: ${ktDeclaration.getElementTextInContext()}")
}
}
override fun <D : FirDeclaration> resolveFirToPhase(declaration: D, toPhase: FirResolvePhase): D {
if (toPhase == FirResolvePhase.RAW_FIR) return declaration
val fileCache = when (val session = declaration.moduleData.session) {
is FirIdeSourcesSession -> session.cache
else -> return declaration
}
return firLazyDeclarationResolver.lazyResolveDeclaration(
firDeclarationToResolve = declaration,
moduleFileCache = fileCache,
scopeSession = ScopeSession(),
toPhase = toPhase,
checkPCE = true,
)
}
override fun <D : FirDeclaration> resolveFirToResolveType(declaration: D, type: ResolveType): D {
if (type == ResolveType.NoResolve) return declaration
val fileCache = when (val session = declaration.moduleData.session) {
is FirIdeSourcesSession -> session.cache
else -> return declaration
}
return firLazyDeclarationResolver.lazyResolveDeclaration(
firDeclaration = declaration,
moduleFileCache = fileCache,
scopeSession = ScopeSession(),
toResolveType = type,
checkPCE = true,
)
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.executeWithoutPCE
internal class FirPhaseRunner {
/**
* We temporary disable multi-locks to fix deadlocks problem
* @see org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LockProvider
*/
inline fun runPhaseWithCustomResolve(@Suppress("UNUSED_PARAMETER") phase: FirResolvePhase, crossinline resolve: () -> Unit) =
runPhaseWithCustomResolveWithoutLock(resolve)
private inline fun runPhaseWithCustomResolveWithoutLock(crossinline resolve: () -> Unit) {
executeWithoutPCE {
resolve()
}
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir
import org.jetbrains.kotlin.fir.ThreadSafeMutableState
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.symbols.FirPhaseManager
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.FirSessionInvalidator
@ThreadSafeMutableState
internal class IdeFirPhaseManager(
private val lazyDeclarationResolver: FirLazyDeclarationResolver,
private val cache: ModuleFileCache,
private val sessionInvalidator: FirSessionInvalidator,
) : FirPhaseManager() {
override fun ensureResolved(
symbol: FirBasedSymbol<*>,
requiredPhase: FirResolvePhase
) {
val fir = symbol.fir
try {
lazyDeclarationResolver.lazyResolveDeclaration(
firDeclarationToResolve = fir,
moduleFileCache = cache,
scopeSession = ScopeSession(),
toPhase = requiredPhase,
checkPCE = true,
skipLocalDeclaration = true,
)
} catch (e: Throwable) {
sessionInvalidator.invalidate(fir.moduleData.session)
throw e
}
}
}
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmKotlinMangler
import org.jetbrains.kotlin.fir.signaturer.FirBasedSignatureComposer
@NoMutableState
data class IdeSessionComponents(val signatureComposer: FirBasedSignatureComposer): FirSessionComponent {
companion object {
fun create(session: FirSession) = IdeSessionComponents(
signatureComposer = FirBasedSignatureComposer(FirJvmKotlinMangler(session))
)
}
}
val FirSession.ideSessionComponents: IdeSessionComponents by FirSession.sessionComponentAccessor()
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.annotations
@RequiresOptIn
annotation class PrivateForInline
@RequiresOptIn
annotation class InternalForInline
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.annotations
@Target(AnnotationTarget.CLASS)
annotation class ThreadSafe
@Target(AnnotationTarget.CLASS)
annotation class NotThreadSafe
@Target(AnnotationTarget.CLASS)
annotation class Immutable
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
internal object DeclarationCopyBuilder {
fun FirSimpleFunction.withBodyFrom(
functionWithBody: FirSimpleFunction,
): FirSimpleFunction = buildSimpleFunctionCopy(this) {
body = functionWithBody.body
symbol = functionWithBody.symbol
initDeclaration(this@withBodyFrom, functionWithBody)
}.apply { reassignAllReturnTargets(functionWithBody) }
fun FirRegularClass.withBodyFrom(
classWithBody: FirRegularClass,
): FirRegularClass = buildRegularClassCopy(this) {
declarations.clear()
declarations.addAll(classWithBody.declarations)
symbol = classWithBody.symbol
initDeclaration(this@withBodyFrom, classWithBody)
resolvePhase = minOf(this.resolvePhase, FirResolvePhase.IMPORTS) //TODO move into initDeclaration?
}
fun FirProperty.withBodyFrom(propertyWithBody: FirProperty): FirProperty {
val originalSetter = this@withBodyFrom.setter
val replacementSetter = propertyWithBody.setter
// setter has a header with `value` parameter, and we want it type to be resolved
val copySetter = if (originalSetter != null && replacementSetter != null) {
buildPropertyAccessorCopy(originalSetter) {
body = replacementSetter.body
symbol = replacementSetter.symbol
initDeclaration(originalSetter, replacementSetter)
}.apply { reassignAllReturnTargets(replacementSetter) }
} else {
replacementSetter
}
val propertyResolvePhase = minOf(
this@withBodyFrom.resolvePhase,
FirResolvePhase.DECLARATIONS,
copySetter?.resolvePhase ?: FirResolvePhase.BODY_RESOLVE,
propertyWithBody.getter?.resolvePhase ?: FirResolvePhase.BODY_RESOLVE,
)
return buildPropertyCopy(this@withBodyFrom) {
symbol = propertyWithBody.symbol
initializer = propertyWithBody.initializer
getter = propertyWithBody.getter
setter = copySetter
if (propertyResolvePhase < FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) {
bodyResolveState = FirPropertyBodyResolveState.NOTHING_RESOLVED
}
initDeclaration(this@withBodyFrom, propertyWithBody)
resolvePhase = propertyResolvePhase
}
}
private fun FirDeclarationBuilder.initDeclaration(
originalDeclaration: FirDeclaration,
builtDeclaration: FirDeclaration,
) {
resolvePhase = minOf(originalDeclaration.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtDeclaration.source
moduleData = originalDeclaration.moduleData
}
private fun FirFunction.reassignAllReturnTargets(from: FirFunction) {
this.accept(object : FirVisitorVoid() {
override fun visitElement(element: FirElement) {
if (element is FirReturnExpression && element.target.labeledElement == from) {
element.target.bind(this@reassignAllReturnTargets)
}
element.acceptChildren(this)
}
})
}
}
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
enum class DeclarationLockType {
READ_LOCK,
WRITE_LOCK,
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
enum class DiagnosticCheckerFilter(val runCommonCheckers: Boolean, val runExtendedCheckers: Boolean) {
ONLY_COMMON_CHECKERS(runCommonCheckers = true, runExtendedCheckers = false),
ONLY_EXTENDED_CHECKERS(runCommonCheckers = false, runExtendedCheckers = true),
EXTENDED_AND_COMMON_CHECKERS(runCommonCheckers = true, runExtendedCheckers = true),
}
@@ -0,0 +1,109 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
import org.jetbrains.kotlin.fir.symbols.impl.LookupTagInternals
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getContainingFile
class FirDeclarationDesignationWithFile(
path: List<FirDeclaration>,
declaration: FirDeclaration,
val firFile: FirFile
) : FirDeclarationDesignation(
path,
declaration,
) {
fun toSequenceWithFile(includeTarget: Boolean): Sequence<FirDeclaration> = sequence {
yield(firFile)
yieldAll(path)
if (includeTarget) yield(declaration)
}
}
open class FirDeclarationDesignation(
val path: List<FirDeclaration>,
val declaration: FirDeclaration,
) {
fun toSequence(includeTarget: Boolean): Sequence<FirDeclaration> = sequence {
yieldAll(path)
if (includeTarget) yield(declaration)
}
}
private fun FirRegularClass.collectForNonLocal(): List<FirDeclaration> {
require(!isLocal)
val firProvider = moduleData.session.firProvider
var containingClassId = classId.outerClassId
val designation = mutableListOf<FirDeclaration>(this)
while (containingClassId != null) {
val currentClass = firProvider.getFirClassifierByFqName(containingClassId) ?: break
designation.add(currentClass)
containingClassId = containingClassId.outerClassId
}
return designation
}
private fun collectDesignationPath(declaration: FirDeclaration): List<FirDeclaration>? {
val containingClass = when (declaration) {
is FirCallableDeclaration -> {
if (declaration.symbol.callableId.isLocal) return null
if ((declaration as? FirCallableDeclaration)?.status?.visibility == Visibilities.Local) return null
when (declaration) {
is FirSimpleFunction, is FirProperty, is FirField, is FirConstructor -> {
val klass = declaration.containingClass() ?: return emptyList()
if (klass.classId.isLocal) return null
@OptIn(LookupTagInternals::class)
klass.toFirRegularClass(declaration.moduleData.session)
}
else -> return null
}
}
is FirClassLikeDeclaration -> {
if (declaration.isLocal) return null
declaration.symbol.classId.outerClassId?.let(declaration.moduleData.session.firProvider::getFirClassifierByFqName)
}
else -> return null
} ?: return emptyList()
require(containingClass is FirRegularClass) {
"FirRegularClass as containing declaration expected but found ${containingClass.renderWithType()}"
}
return if (!containingClass.isLocal) containingClass.collectForNonLocal().asReversed() else null
}
fun FirDeclaration.collectDesignation(firFile: FirFile): FirDeclarationDesignationWithFile =
tryCollectDesignation(firFile) ?: error("No designation of local declaration ${this.render()}")
fun FirDeclaration.collectDesignation(): FirDeclarationDesignation =
tryCollectDesignation() ?: error("No designation of local declaration ${this.render()}")
fun FirDeclaration.collectDesignationWithFile(): FirDeclarationDesignationWithFile =
tryCollectDesignationWithFile() ?: error("No designation of local declaration ${this.render()}")
fun FirDeclaration.tryCollectDesignation(firFile: FirFile): FirDeclarationDesignationWithFile? =
collectDesignationPath(this)?.let {
FirDeclarationDesignationWithFile(it, this, firFile)
}
fun FirDeclaration.tryCollectDesignation(): FirDeclarationDesignation? =
collectDesignationPath(this)?.let {
FirDeclarationDesignation(it, this)
}
fun FirDeclaration.tryCollectDesignationWithFile(): FirDeclarationDesignationWithFile? {
val path = collectDesignationPath(this) ?: return null
val firFile = getContainingFile() ?: return null
return FirDeclarationDesignationWithFile(path, this, firFile)
}
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveType
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtLambdaExpression
abstract class FirModuleResolveState {
abstract val project: Project
abstract val rootModuleSession: FirSession
abstract val moduleInfo: ModuleInfo
internal abstract fun getSessionFor(moduleInfo: ModuleInfo): FirSession
/**
* Build fully resolved FIR node for requested element.
* This operation could be performance affective because it create FIleStructureElement and resolve non-local declaration into BODY phase, use
* @see tryGetCachedFirFile to get [FirFile] in undefined phase
*/
internal abstract fun getOrBuildFirFor(element: KtElement): FirElement?
/**
* Get or build or get cached [FirFile] for requested file in undefined phase
*/
internal abstract fun getOrBuildFirFile(ktFile: KtFile): FirFile
/**
* Try get [FirFile] from the cache in undefined phase
*/
internal abstract fun tryGetCachedFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile?
internal abstract fun getDiagnostics(element: KtElement, filter: DiagnosticCheckerFilter): List<FirPsiDiagnostic>
internal abstract fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic>
@InternalForInline
abstract fun findSourceFirDeclaration(
ktDeclaration: KtDeclaration,
): FirDeclaration
@InternalForInline
abstract fun findSourceFirDeclaration(
ktDeclaration: KtLambdaExpression,
): FirDeclaration
/**
* Looks for compiled non-local [ktDeclaration] declaration by querying its classId/callableId from the SymbolProvider.
*
* Works only if [ktDeclaration] is compiled (i.e. comes from .class file).
*/
@InternalForInline
abstract fun findSourceFirCompiledDeclaration(
ktDeclaration: KtDeclaration
): FirDeclaration
internal abstract fun <D : FirDeclaration> resolveFirToPhase(declaration: D, toPhase: FirResolvePhase): D
internal abstract fun <D : FirDeclaration> resolveFirToResolveType(declaration: D, type: ResolveType): D
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementFinder
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider
import org.jetbrains.kotlin.fir.deserialization.ModuleDataProvider
import org.jetbrains.kotlin.fir.java.FirJavaElementFinder
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.FirIdeSourcesSession
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
import org.jetbrains.kotlin.psi.KtElement
abstract class FirModuleResolveStateConfigurator {
abstract fun createPackagePartsProvider(moduleInfo: ModuleSourceInfoBase, scope: GlobalSearchScope): PackagePartProvider
abstract fun createModuleDataProvider(moduleInfo: ModuleSourceInfoBase): ModuleDataProvider
abstract fun getLanguageVersionSettings(moduleInfo: ModuleSourceInfoBase): LanguageVersionSettings
abstract fun getModuleSourceScope(moduleInfo: ModuleSourceInfoBase): GlobalSearchScope
abstract fun createScopeForModuleLibraries(moduleInfo: ModuleSourceInfoBase): GlobalSearchScope
abstract fun createSealedInheritorsProvider(): SealedClassInheritorsProvider
abstract fun configureSourceSession(session: FirSession)
}
val Project.stateConfigurator: FirModuleResolveStateConfigurator
get() = ServiceManager.getService(this, FirModuleResolveStateConfigurator::class.java)
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.psi.KtElement
import java.util.*
import kotlin.reflect.KClass
class InvalidFirElementTypeException(
actualFirClass: KClass<out FirElement>?,
ktElement: KtElement?,
expectedFirClasses: List<KClass<out FirElement>>,
) : IllegalStateException() {
override val message: String = buildString {
if (ktElement != null) {
append("For $ktElement with text `${ktElement.text}`, ")
}
val message = when (expectedFirClasses.size) {
0 -> "Unexpected FirElement of type:"
1 -> "The FirElement of type ${expectedFirClasses.single()} expected, but"
else -> "One of [${expectedFirClasses.joinToString()}] FirElement types expected, but"
}
append(if (ktElement == null) message else message.replaceFirstChar { it.lowercase(Locale.getDefault()) })
if (actualFirClass != null) {
append(" ${actualFirClass.simpleName} found")
} else {
append(" no FirElement found")
}
}
}
fun throwUnexpectedFirElementError(
firElement: FirElement?,
ktElement: KtElement? = null,
vararg expectedFirClasses: KClass<out FirElement>
): Nothing {
throw InvalidFirElementTypeException(firElement?.let { it::class }, ktElement, expectedFirClasses.toList())
}
@@ -0,0 +1,209 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.builder.BodyBuildingMode
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.createEmptySession
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.types.Variance
// TODO replace with structural type comparison?
object KtDeclarationAndFirDeclarationEqualityChecker {
fun representsTheSameDeclaration(psi: KtFunction, fir: FirFunction): Boolean {
if ((fir.receiverTypeRef != null) != (psi.receiverTypeReference != null)) return false
if (fir.receiverTypeRef != null
&& !isTheSameTypes(
psi.receiverTypeReference!!,
fir.receiverTypeRef!!,
isVararg = false
)
) {
return false
}
if (fir.valueParameters.size != psi.valueParameters.size) return false
fir.valueParameters.zip(psi.valueParameters) { expectedParameter, candidateParameter ->
if (expectedParameter.name.toString() != candidateParameter.name) return false
if (expectedParameter.isVararg != candidateParameter.isVarArg) return false
val candidateParameterType = candidateParameter.typeReference ?: return false
if (!isTheSameTypes(
candidateParameterType,
expectedParameter.returnTypeRef,
isVararg = expectedParameter.isVararg
)
) {
return false
}
}
return true
}
@OptIn(ExperimentalStdlibApi::class)
private fun FirTypeRef.renderTypeAsKotlinType(isVararg: Boolean = false): String {
val rendered = when (this) {
is FirResolvedTypeRef -> type.renderTypeAsKotlinType()
is FirUserTypeRef -> {
val renderedQualifier = qualifier.joinToString(separator = ".") { part ->
buildString {
append(part.name)
if (part.typeArgumentList.typeArguments.isNotEmpty()) {
part.typeArgumentList.typeArguments.joinTo(this, prefix = "<", postfix = ">") { it.renderTypeAsKotlinType() }
}
}
}
if (isMarkedNullable) "$renderedQualifier?" else renderedQualifier
}
is FirFunctionTypeRef -> {
val classId = if (isSuspend) {
StandardNames.getSuspendFunctionClassId(parametersCount)
} else {
StandardNames.getFunctionClassId(parametersCount)
}
buildString {
append(classId.asSingleFqName().toString())
val parameters = buildList {
receiverTypeRef?.let(::add)
valueParameters.mapTo(this) { it.returnTypeRef }
returnTypeRef.let(::add)
}
if (parameters.isNotEmpty()) {
append(parameters.joinToString(prefix = "<", postfix = ">") { it.renderTypeAsKotlinType() })
}
}
}
else -> error("Invalid type reference $this")
}
return if (isVararg) {
rendered.asArrayType()
} else {
rendered
}
}
private fun String.asArrayType(): String {
classIdToName[this]?.let { return it }
return "kotlin.Array<out $this>"
}
@OptIn(ExperimentalStdlibApi::class)
private val classIdToName: Map<String, String> = buildList<Pair<String, String>> {
StandardClassIds.primitiveArrayTypeByElementType.mapTo(this) { (classId, arrayClassId) ->
classId.asString().replace('/', '.') to arrayClassId.asString().replace('/', '.')
}
StandardClassIds.unsignedArrayTypeByElementType.mapTo(this) { (classId, arrayClassId) ->
classId.asString().replace('/', '.') to arrayClassId.asString().replace('/', '.')
}
}.toMap()
private fun FirTypeProjection.renderTypeAsKotlinType() = when (this) {
is FirStarProjection -> "*"
is FirTypeProjectionWithVariance -> buildString {
append(variance.label)
if (variance != Variance.INVARIANT) {
append(" ")
}
append(typeRef.renderTypeAsKotlinType())
}
else -> error("Invalid type projection $this")
}
private fun isTheSameTypes(
psiTypeReference: KtTypeReference,
coneTypeReference: FirTypeRef,
isVararg: Boolean
): Boolean =
psiTypeReference.toKotlinTypReference().renderTypeAsKotlinType(isVararg) == coneTypeReference.renderTypeAsKotlinType()
@Suppress("DEPRECATION_ERROR")
private fun KtTypeReference.toKotlinTypReference(): FirTypeRef {
// Maybe resolve all types here to not to work with FirTypeRef directly
return RawFirBuilder(
createEmptySession(),
DummyScopeProvider,
psiMode = PsiHandlingMode.IDE,
bodyBuildingMode = BodyBuildingMode.NORMAL
).buildTypeReference(this)
}
private fun ConeKotlinType.renderTypeAsKotlinType(): String {
val rendered = when (this) {
is ConeClassLikeType -> buildString {
append(lookupTag.classId.asString())
if (typeArguments.isNotEmpty()) {
append(typeArguments.joinToString(prefix = "<", postfix = ">", separator = ", ") { it.renderTypeAsKotlinType() })
}
}
is ConeTypeVariableType -> lookupTag.name.asString()
is ConeLookupTagBasedType -> lookupTag.name.asString()
is ConeFlexibleType -> {
// Can be present as return type
"${lowerBound.renderTypeAsKotlinType()}..${upperBound.renderTypeAsKotlinType()}"
}
else -> error("Type $this should not be present in Kotlin declaration")
}.replace('/', '.')
return rendered + nullability.suffix
}
private fun ConeTypeProjection.renderTypeAsKotlinType(): String = when (this) {
ConeStarProjection -> "*"
is ConeKotlinTypeProjectionIn -> "in ${type.renderTypeAsKotlinType()}"
is ConeKotlinTypeProjectionOut -> "out ${type.renderTypeAsKotlinType()}"
is ConeKotlinTypeConflictingProjection -> "CONFLICTING-PROJECTION ${type.renderTypeAsKotlinType()}"
is ConeKotlinType -> renderTypeAsKotlinType()
}
@TestOnly
fun renderPsi(ktFunction: KtFunction): String = buildString {
appendLine("receiver: ${ktFunction.receiverTypeReference?.toKotlinTypReference()?.renderTypeAsKotlinType()}")
ktFunction.valueParameters.forEach { parameter ->
appendLine("${parameter.name}: ${parameter.typeReference?.toKotlinTypReference()?.renderTypeAsKotlinType()}")
}
appendLine("return: ${ktFunction.typeReference?.toKotlinTypReference()?.renderTypeAsKotlinType()}")
}
@TestOnly
fun renderFir(firFunction: FirFunction): String = buildString {
appendLine("receiver: ${firFunction.receiverTypeRef?.renderTypeAsKotlinType()}")
firFunction.valueParameters.forEach { parameter ->
appendLine("${parameter.name}: ${parameter.returnTypeRef.renderTypeAsKotlinType()}")
}
appendLine("return: ${firFunction.returnTypeRef.renderTypeAsKotlinType()}")
}
private object DummyScopeProvider : FirScopeProvider() {
override fun getUseSiteMemberScope(klass: FirClass, useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope {
shouldNotBeCalled()
}
override fun getStaticMemberScopeForCallables(
klass: FirClass,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirScope? {
shouldNotBeCalled()
}
override fun getNestedClassifierScope(klass: FirClass, useSiteSession: FirSession, scopeSession: ScopeSession): FirScope? {
shouldNotBeCalled()
}
private fun shouldNotBeCalled(): Nothing =
error("Should not be called in RawFirBuilder while converting KtTypeReference")
}
}
@@ -0,0 +1,223 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.providers.getModuleInfo
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirIdeResolveStateService
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveType
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtLambdaExpression
import kotlin.reflect.KClass
/**
* Returns [FirModuleResolveState] which corresponds to containing module
*/
fun KtElement.getResolveState(): FirModuleResolveState =
getModuleInfo().getResolveState(project)
/**
* Returns [FirModuleResolveState] which corresponds to containing module
*/
fun ModuleInfo.getResolveState(project: Project): FirModuleResolveState =
FirIdeResolveStateService.getInstance(project).getResolveState(this)
/**
* Creates [FirDeclaration] by [KtDeclaration] and executes an [action] on it
* [FirDeclaration] passed to [action] will be resolved at least to [phase] when executing [action] on it
*
* [FirDeclaration] passed to [action] should not be leaked outside [action] lambda
* Otherwise, some threading problems may arise,
*/
@OptIn(InternalForInline::class)
inline fun <R> KtDeclaration.withFirDeclaration(
resolveState: FirModuleResolveState,
phase: FirResolvePhase = FirResolvePhase.RAW_FIR,
action: (FirDeclaration) -> R
): R {
val firDeclaration = if (containingKtFile.isCompiled) {
resolveState.findSourceFirCompiledDeclaration(this)
} else {
resolveState.findSourceFirDeclaration(this)
}
val resolvedDeclaration = if (firDeclaration.resolvePhase < phase) {
firDeclaration.resolvedFirToPhase(phase, resolveState)
} else {
firDeclaration
}
return action(resolvedDeclaration)
}
/**
* Creates [FirDeclaration] by [KtDeclaration] and executes an [action] on it
* [FirDeclaration] passed to [action] will be resolved at least to [resolveType] when executing [action] on it
*
* [FirDeclaration] passed to [action] should not be leaked outside [action] lambda
* Otherwise, some threading problems may arise,
*/
@OptIn(InternalForInline::class)
inline fun <R> KtDeclaration.withFirDeclaration(
resolveState: FirModuleResolveState,
resolveType: ResolveType = ResolveType.NoResolve,
action: (FirDeclaration) -> R
): R {
val firDeclaration = resolveState.findSourceFirDeclaration(this)
val resolvedDeclaration = firDeclaration.resolvedFirToType(resolveType, resolveState)
return action(resolvedDeclaration)
}
/**
* Creates [FirDeclaration] by [KtDeclaration] and executes an [action] on it
* [FirDeclaration] passed to [action] will be resolved at least to [phase] when executing [action] on it
*
* If resulted [FirDeclaration] is not [F] throws [InvalidFirElementTypeException]
*
* [FirDeclaration] passed to [action] should not be leaked outside [action] lambda
* Otherwise, some threading problems may arise,
*/
@OptIn(InternalForInline::class)
inline fun <reified F : FirDeclaration, R> KtDeclaration.withFirDeclarationOfType(
resolveState: FirModuleResolveState,
phase: FirResolvePhase = FirResolvePhase.RAW_FIR,
action: (F) -> R
): R = withFirDeclaration(resolveState, phase) { firDeclaration ->
if (firDeclaration !is F) throwUnexpectedFirElementError(firDeclaration, this, F::class)
action(firDeclaration)
}
/**
* Creates [FirDeclaration] by [KtLambdaExpression] and executes an [action] on it
*
* If resulted [FirDeclaration] is not [F] throws [InvalidFirElementTypeException]
*
* [FirDeclaration] passed to [action] should not be leaked outside [action] lambda
* Otherwise, some threading problems may arise,
*/
@OptIn(InternalForInline::class)
inline fun <reified F : FirDeclaration, R> KtLambdaExpression.withFirDeclarationOfType(
resolveState: FirModuleResolveState,
action: (F) -> R
): R {
val firDeclaration = resolveState.findSourceFirDeclaration(this)
if (firDeclaration !is F) throwUnexpectedFirElementError(firDeclaration, this, F::class)
return action(firDeclaration)
}
/**
* Executes [action] with given [FirDeclaration] under read action, so resolve **is not possible** inside [action]
* [FirDeclaration] passed to [action] will be resolved at least to [phase] when executing [action] on it
*/
fun <D : FirDeclaration, R> D.withFirDeclaration(
resolveState: FirModuleResolveState,
phase: FirResolvePhase = FirResolvePhase.RAW_FIR,
action: (D) -> R,
): R {
val resolvedDeclaration = resolvedFirToPhase(phase, resolveState)
return action(resolvedDeclaration)
}
/**
* Executes [action] with given [FirDeclaration] under read action, so resolve **is not possible** inside [action]
* [FirDeclaration] passed to [action] will be resolved at least to [phase] when executing [action] on it
*/
fun <D : FirDeclaration, R> D.withFirDeclaration(
type: ResolveType,
resolveState: FirModuleResolveState,
action: (D) -> R,
): R {
val resolvedDeclaration = resolvedFirToType(type, resolveState)
return action(resolvedDeclaration)
}
/**
* Returns a list of Diagnostics compiler finds for given [KtElement]
* This operation could be performance affective because it create FIleStructureElement and resolve non-local declaration into BODY phase
*/
fun KtElement.getDiagnostics(resolveState: FirModuleResolveState, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic> =
resolveState.getDiagnostics(this, filter)
/**
* Returns a list of Diagnostics compiler finds for given [KtFile]
* This operation could be performance affective because it create FIleStructureElement and resolve non-local declaration into BODY phase
*/
fun KtFile.collectDiagnosticsForFile(
resolveState: FirModuleResolveState,
filter: DiagnosticCheckerFilter
): Collection<FirPsiDiagnostic> =
resolveState.collectDiagnosticsForFile(this, filter)
/**
* Resolves a given [FirDeclaration] to [phase] and returns resolved declaration
*
* Should not be called form [withFirDeclaration], [withFirDeclarationOfType] functions, as it it may cause deadlock
*/
fun <D : FirDeclaration> D.resolvedFirToPhase(
phase: FirResolvePhase,
resolveState: FirModuleResolveState
): D =
resolveState.resolveFirToPhase(this, phase)
/**
* Resolves a given [FirDeclaration] to [phase] and returns resolved declaration
*
* Should not be called form [withFirDeclaration], [withFirDeclarationOfType] functions, as it it may cause deadlock
*/
fun <D : FirDeclaration> D.resolvedFirToType(
type: ResolveType,
resolveState: FirModuleResolveState
): D =
resolveState.resolveFirToResolveType(this, type)
/**
* Get a [FirElement] which was created by [KtElement]
* Returned [FirElement] is guaranteed to be resolved to [FirResolvePhase.BODY_RESOLVE] phase
* This operation could be performance affective because it create FIleStructureElement and resolve non-local declaration into BODY phase.
*
* The `null` value is returned iff FIR tree does not have corresponding element
*/
fun KtElement.getOrBuildFir(
resolveState: FirModuleResolveState,
): FirElement? = resolveState.getOrBuildFirFor(this)
/**
* Get a [FirElement] which was created by [KtElement], but only if it is subtype of [E], `null` otherwise
* Returned [FirElement] is guaranteed to be resolved to [FirResolvePhase.BODY_RESOLVE] phase
* This operation could be performance affective because it create FIleStructureElement and resolve non-local declaration into BODY phase
*/
inline fun <reified E : FirElement> KtElement.getOrBuildFirSafe(
resolveState: FirModuleResolveState,
) = getOrBuildFir(resolveState) as? E
/**
* Get a [FirElement] which was created by [KtElement], but only if it is subtype of [E], throws [InvalidFirElementTypeException] otherwise
* Returned [FirElement] is guaranteed to be resolved to [FirResolvePhase.BODY_RESOLVE] phase
* This operation could be performance affective because it create FIleStructureElement and resolve non-local declaration into BODY phase
*/
inline fun <reified E : FirElement> KtElement.getOrBuildFirOfType(
resolveState: FirModuleResolveState,
): E {
val fir = this.getOrBuildFir(resolveState)
if (fir is E) return fir
throwUnexpectedFirElementError(fir, this, E::class)
}
/**
* Get a [FirFile] which was created by [KtElement]
* Returned [FirFile] can be resolved to any phase from [FirResolvePhase.RAW_FIR] to [FirResolvePhase.BODY_RESOLVE]
*/
fun KtFile.getOrBuildFirFile(resolveState: FirModuleResolveState): FirFile =
resolveState.getOrBuildFirFile(this)
@@ -0,0 +1,344 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.analysis.providers.getModuleInfo
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.realPsi
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.asTowerDataElement
import org.jetbrains.kotlin.fir.resolve.transformers.FirTypeResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.fir.scopes.createImportingScopes
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirModuleResolveStateDepended
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirModuleResolveStateImpl
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DeclarationCopyBuilder.withBodyFrom
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FileTowerProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerContextProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerDataContextAllElementsCollector
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.runCustomResolveUnderLock
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.FirElementsRecorder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.KtToFirMapping
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.RawFirNonLocalDeclarationBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.RawFirReplacement
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.buildFileFirAnnotation
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.buildFirUserTypeRef
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firIdeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.FirIdeSourcesSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getElementTextInContext
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.parentOfType
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.parentsOfType
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
object LowLevelFirApiFacadeForResolveOnAir {
private fun KtDeclaration.canBeEnclosingDeclaration(): Boolean = when (this) {
is KtNamedFunction -> isTopLevel || containingClassOrObject?.isLocal == false
is KtProperty -> isTopLevel || containingClassOrObject?.isLocal == false
is KtClassOrObject -> !isLocal
is KtTypeAlias -> isTopLevel() || containingClassOrObject?.isLocal == false
else -> false
}
private fun findEnclosingNonLocalDeclaration(position: KtElement): KtNamedDeclaration? =
position.parentsOfType<KtNamedDeclaration>().firstOrNull { ktDeclaration ->
ktDeclaration.canBeEnclosingDeclaration()
}
private fun recordOriginalDeclaration(targetDeclaration: KtNamedDeclaration, originalDeclaration: KtNamedDeclaration) {
require(!targetDeclaration.isPhysical)
require(originalDeclaration.containingKtFile !== targetDeclaration.containingKtFile)
val originalDeclarationParents = originalDeclaration.parentsOfType<KtDeclaration>().toList()
val fakeDeclarationParents = targetDeclaration.parentsOfType<KtDeclaration>().toList()
originalDeclarationParents.zip(fakeDeclarationParents) { original, fake ->
fake.originalDeclaration = original
}
}
fun <T : KtElement> onAirResolveElement(
state: FirModuleResolveState,
place: T,
elementToResolve: T,
): FirElement {
require(state is FirModuleResolveStateImpl)
require(place.isPhysical)
val declaration = runBodyResolveOnAir(
state = state,
replacement = RawFirReplacement(place, elementToResolve),
onAirCreatedDeclaration = true
)
val expressionLocator = object : FirVisitorVoid() {
var result: FirElement? = null
private set
override fun visitElement(element: FirElement) {
if (element.realPsi == elementToResolve) result = element
if (result != null) return
element.acceptChildren(this)
}
}
declaration.accept(expressionLocator)
return expressionLocator.result ?: error("Resolved on-air element was not found in containing declaration")
}
fun onAirGetTowerContextProvider(
state: FirModuleResolveState,
place: KtElement,
): FirTowerContextProvider {
require(state is FirModuleResolveStateImpl)
require(place.isPhysical)
return if (place is KtFile) {
FileTowerProvider(place, onAirGetTowerContextForFile(state, place))
} else {
val validPlace = PsiTreeUtil.findFirstParent(place, false) {
RawFirReplacement.isApplicableForReplacement(it as KtElement)
} as KtElement
FirTowerDataContextAllElementsCollector().also {
runBodyResolveOnAir(
state = state,
collector = it,
onAirCreatedDeclaration = false,
replacement = RawFirReplacement(validPlace, validPlace),
)
}
}
}
private fun onAirGetTowerContextForFile(
state: FirModuleResolveStateImpl,
file: KtFile,
): FirTowerDataContext {
require(file.isPhysical)
val session = state.getSessionFor(file.getModuleInfo()) as FirIdeSourcesSession
val firFile = session.firFileBuilder.buildRawFirFileWithCaching(
ktFile = file,
cache = session.cache,
preferLazyBodies = true
)
state.firLazyDeclarationResolver.lazyResolveFileDeclaration(
firFile = firFile,
moduleFileCache = session.cache,
scopeSession = ScopeSession(),
toPhase = FirResolvePhase.IMPORTS
)
val importingScopes = createImportingScopes(firFile, firFile.moduleData.session, ScopeSession(), useCaching = false)
val fileScopeElements = importingScopes.map { it.asTowerDataElement(isLocal = false) }
return FirTowerDataContext().addNonLocalTowerDataElements(fileScopeElements)
}
fun getResolveStateForDependentCopy(
originalState: FirModuleResolveState,
originalKtFile: KtFile,
elementToAnalyze: KtElement
): FirModuleResolveState {
require(originalState is FirModuleResolveStateImpl)
require(elementToAnalyze !is KtFile) { "KtFile for dependency element not supported" }
require(!elementToAnalyze.isPhysical) { "Depended state should be build only for non-physical elements" }
val dependencyNonLocalDeclaration = findEnclosingNonLocalDeclaration(elementToAnalyze)
?: return FirModuleResolveStateDepended(
originalState,
FileTowerProvider(elementToAnalyze.containingKtFile, onAirGetTowerContextForFile(originalState, originalKtFile)),
ktToFirMapping = null
)
val sameDeclarationInOriginalFile = PsiTreeUtil.findSameElementInCopy(dependencyNonLocalDeclaration, originalKtFile)
?: error("Cannot find original function matching to ${dependencyNonLocalDeclaration.getElementTextInContext()} in $originalKtFile")
recordOriginalDeclaration(
targetDeclaration = dependencyNonLocalDeclaration,
originalDeclaration = sameDeclarationInOriginalFile
)
val collector = FirTowerDataContextAllElementsCollector()
val copiedFirDeclaration = runBodyResolveOnAir(
originalState,
replacement = RawFirReplacement(sameDeclarationInOriginalFile, dependencyNonLocalDeclaration),
onAirCreatedDeclaration = true,
collector = collector,
)
val mapping = KtToFirMapping(copiedFirDeclaration, FirElementsRecorder())
return FirModuleResolveStateDepended(originalState, collector, mapping)
}
private fun tryResolveAsFileAnnotation(
annotationEntry: KtAnnotationEntry,
state: FirModuleResolveStateImpl,
replacement: RawFirReplacement,
firFile: FirFile,
collector: FirTowerDataContextCollector? = null,
): FirAnnotation {
val annotationCall = buildFileFirAnnotation(
session = firFile.moduleData.session,
baseScopeProvider = firFile.moduleData.session.firIdeProvider.kotlinScopeProvider,
fileAnnotation = annotationEntry,
replacement = replacement
)
state.firLazyDeclarationResolver.resolveFileAnnotations(
firFile = firFile,
annotations = listOf(annotationCall),
moduleFileCache = state.rootModuleSession.cache,
scopeSession = ScopeSession(),
checkPCE = true,
collector = collector
)
return annotationCall
}
private fun runBodyResolveOnAir(
state: FirModuleResolveStateImpl,
replacement: RawFirReplacement,
onAirCreatedDeclaration: Boolean,
collector: FirTowerDataContextCollector? = null,
): FirElement {
val nonLocalDeclaration = findEnclosingNonLocalDeclaration(replacement.from)
val originalFirFile = state.getOrBuildFirFile(replacement.from.containingKtFile)
if (nonLocalDeclaration == null) {
//It is possible that it is file annotation is going to resolve
val annotationCall = replacement.from.parentOfType<KtAnnotationEntry>(withSelf = true)
if (annotationCall != null) {
return tryResolveAsFileAnnotation(
annotationEntry = annotationCall,
state = state,
replacement = replacement,
firFile = originalFirFile,
collector = collector,
)
} else {
error("Cannot find enclosing declaration for ${replacement.from.getElementTextInContext()}")
}
}
val originalDeclaration = nonLocalDeclaration.getOrBuildFirOfType<FirDeclaration>(state)
val originalDesignation = originalDeclaration.collectDesignation()
val newDeclarationWithReplacement = RawFirNonLocalDeclarationBuilder.buildWithReplacement(
session = originalDeclaration.moduleData.session,
scopeProvider = originalDeclaration.moduleData.session.firIdeProvider.kotlinScopeProvider,
designation = originalDesignation,
rootNonLocalDeclaration = nonLocalDeclaration,
replacement = replacement,
)
val isInBodyReplacement = isInBodyReplacement(nonLocalDeclaration, replacement)
return state.rootModuleSession.cache.firFileLockProvider.runCustomResolveUnderLock(originalFirFile, true) {
val copiedFirDeclaration = if (isInBodyReplacement) {
when (originalDeclaration) {
is FirSimpleFunction ->
originalDeclaration.withBodyFrom(newDeclarationWithReplacement as FirSimpleFunction)
is FirProperty ->
originalDeclaration.withBodyFrom(newDeclarationWithReplacement as FirProperty)
is FirRegularClass ->
originalDeclaration.withBodyFrom(newDeclarationWithReplacement as FirRegularClass)
is FirTypeAlias -> newDeclarationWithReplacement
else -> error("Not supported type ${originalDeclaration::class.simpleName}")
}
} else newDeclarationWithReplacement
val onAirDesignation = FirDeclarationDesignationWithFile(
path = originalDesignation.path,
declaration = copiedFirDeclaration,
firFile = originalFirFile
)
ResolveTreeBuilder.resolveEnsure(onAirDesignation.declaration, FirResolvePhase.BODY_RESOLVE) {
state.firLazyDeclarationResolver.runLazyDesignatedOnAirResolveToBodyWithoutLock(
designation = onAirDesignation,
moduleFileCache = state.rootModuleSession.cache,
checkPCE = true,
onAirCreatedDeclaration = onAirCreatedDeclaration,
towerDataContextCollector = collector,
)
}
copiedFirDeclaration
}
}
private fun isInBodyReplacement(ktDeclaration: KtDeclaration, replacement: RawFirReplacement): Boolean = when (ktDeclaration) {
is KtNamedFunction ->
ktDeclaration.bodyBlockExpression?.let { it.isAncestor(replacement.from, true) } ?: false
is KtProperty -> {
val insideGetterBody = ktDeclaration.getter?.bodyBlockExpression?.let {
it.isAncestor(replacement.from, true)
} ?: false
val insideGetterOrSetterBody = insideGetterBody || ktDeclaration.setter?.bodyBlockExpression?.let {
it.isAncestor(replacement.from, true)
} ?: false
insideGetterOrSetterBody || ktDeclaration.initializer?.let {
it.isAncestor(replacement.from, true)
} ?: false
}
is KtClassOrObject ->
ktDeclaration.body?.let { it.isAncestor(replacement.from, true) } ?: false
is KtTypeAlias -> false
else -> error("Not supported type ${ktDeclaration::class.simpleName}")
}
fun onAirResolveTypeInPlace(
place: KtElement,
typeReference: KtTypeReference,
state: FirModuleResolveState
): FirResolvedTypeRef {
val context = state.getTowerContextProvider().getClosestAvailableParentContext(place)
?: error("TowerContext not found for ${place.getElementTextInContext()}")
val session = state.rootModuleSession
val firTypeReference = buildFirUserTypeRef(
typeReference = typeReference,
session = session,
baseScopeProvider = session.firIdeProvider.kotlinScopeProvider
)
return FirTypeResolveTransformer(
session = session,
scopeSession = ScopeSession(),
initialScopes = context.towerDataElements.asReversed().mapNotNull { it.scope }
).transformTypeRef(firTypeReference, null)
}
private class TowerProviderForElementForState(private val state: FirModuleResolveState) : FirTowerContextProvider {
override fun getClosestAvailableParentContext(ktElement: KtElement): FirTowerDataContext? {
return if (ktElement.isPhysical) {
onAirGetTowerContextProvider(state, ktElement).getClosestAvailableParentContext(ktElement)
} else {
require(state is FirModuleResolveStateDepended) {
"Invalid resolve state ${this::class.simpleName} but have to be ${FirModuleResolveStateDepended::class.simpleName}"
}
state.towerProviderBuiltUponElement.getClosestAvailableParentContext(ktElement)
}
}
}
fun FirModuleResolveState.getTowerContextProvider(): FirTowerContextProvider =
TowerProviderForElementForState(this)
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.psi.KtElement
abstract class ModuleInfoProvider {
companion object {
fun getInstance(project: Project): ModuleInfoProvider = ServiceManager.getService(project, ModuleInfoProvider::class.java)
}
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.FirIdeModuleSession
/**
* Returns a [GlobalSearchScope] declarations from which [FirSession] knows about
*/
val FirSession.searchScope: GlobalSearchScope
get() {
check(this is FirIdeModuleSession)
return scope
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.fir.caches
import org.jetbrains.kotlin.fir.caches.FirCache
import java.util.concurrent.ConcurrentHashMap
internal class FirThreadSafeCache<K : Any, V, CONTEXT>(
private val createValue: (K, CONTEXT) -> V
) : FirCache<K, V, CONTEXT>() {
private val map = ConcurrentHashMap<K, Any>()
override fun getValue(key: K, context: CONTEXT): V =
map.getOrPutWithNullableValue(key) { createValue(it, context) }
override fun getValueIfComputed(key: K): V? =
map[key]?.nullValueToNull()
}
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.fir.caches
import org.jetbrains.kotlin.fir.caches.FirCache
import java.util.concurrent.ConcurrentHashMap
internal class FirThreadSafeCacheWithPostCompute<K : Any, V, CONTEXT, DATA>(
private val createValue: (K, CONTEXT) -> Pair<V, DATA>,
private val postCompute: (K, V, DATA) -> Unit
) : FirCache<K, V, CONTEXT>() {
private val map = ConcurrentHashMap<K, ValueWithPostCompute<K, V, DATA>>()
@Suppress("UNCHECKED_CAST")
override fun getValue(key: K, context: CONTEXT): V =
map.getOrPut(key) {
ValueWithPostCompute(
key,
calculate = { createValue(it, context) },
postCompute = postCompute
)
}.getValue()
override fun getValueIfComputed(key: K): V? =
map[key]?.getValueIfComputed()
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.fir.caches
import org.jetbrains.kotlin.fir.caches.*
object FirThreadSafeCachesFactory : FirCachesFactory() {
override fun <KEY : Any, VALUE, CONTEXT> createCache(createValue: (KEY, CONTEXT) -> VALUE): FirCache<KEY, VALUE, CONTEXT> =
FirThreadSafeCache(createValue)
override fun <KEY : Any, VALUE, CONTEXT, DATA> createCacheWithPostCompute(
createValue: (KEY, CONTEXT) -> Pair<VALUE, DATA>,
postCompute: (KEY, VALUE, DATA) -> Unit
): FirCache<KEY, VALUE, CONTEXT> =
FirThreadSafeCacheWithPostCompute(createValue, postCompute)
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.fir.caches
import java.util.concurrent.ConcurrentMap
internal object NullValue
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
internal inline fun <VALUE> Any.nullValueToNull(): VALUE = when (this) {
NullValue -> null
else -> this
} as VALUE
internal inline fun <KEY : Any, RESULT> ConcurrentMap<KEY, Any>.getOrPutWithNullableValue(
key: KEY,
crossinline compute: (KEY) -> Any?
): RESULT {
val value = getOrPut(key) { compute(key) ?: NullValue }
return value.nullValueToNull()
}
@@ -0,0 +1,107 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.fir.caches
/**
* Lazily calculated value which runs postCompute in the same thread,
* assuming that postCompute may try to read that value inside current thread,
* So in the period then value is calculated but post compute was not finished,
* only thread that initiated the calculating may see the value,
* other threads will have to wait wait until that value is calculated
*/
internal class ValueWithPostCompute<KEY, VALUE, DATA>(
/**
* We need at least one final field to be written in constructor to guarantee safe initialization of our [ValueWithPostCompute]
*/
private val key: KEY,
calculate: (KEY) -> Pair<VALUE, DATA>,
postCompute: (KEY, VALUE, DATA) -> Unit,
) {
private var _calculate: ((KEY) -> Pair<VALUE, DATA>)? = calculate
private var _postCompute: ((KEY, VALUE, DATA) -> Unit)? = postCompute
/**
* can be in one of the following three states:
* [ValueIsNotComputed] -- value is not initialized and thread are now executing [_postCompute]
* [ExceptionWasThrownDuringValueComputation] -- exception was thrown during value computation, it will be rethrown on every value access
* [ValueIsPostComputingNow] -- thread with threadId has computed the value and only it can access it during post compute
* some value of type [VALUE] -- value is computed and post compute was executed, values is visible for all threads
*
* Value may be set only under [LazyValueWithPostCompute] intrinsic lock hold
* And may be read from any thread
*/
@Volatile
private var value: Any? = ValueIsNotComputed
private val guard = ThreadLocal.withInitial { false }
private inline fun <T> recursiveGuarded(body: () -> T): T {
check(!guard.get()) {
"Should not be called recursively"
}
guard.set(true)
return try {
body()
} finally {
guard.set(false)
}
}
@Suppress("UNCHECKED_CAST")
fun getValue(): VALUE {
when (val stateSnapshot = value) {
is ValueIsPostComputingNow -> {
if (stateSnapshot.threadId == Thread.currentThread().id) {
return stateSnapshot.value as VALUE
} else {
synchronized(this) { // wait until other thread which holds the lock now computes the value
return value as VALUE
}
}
}
ValueIsNotComputed -> synchronized(this) {
// if we entered synchronized section that's mean that the value is not yet calculated and was not started to be calculated
// or the some other thread calculated the value while we were waiting to acquire the lock
if (value != ValueIsNotComputed) { // some other thread calculated our value
return value as VALUE
}
val calculatedValue = try {
val (calculated, data) = recursiveGuarded {
_calculate!!(key)
}
value = ValueIsPostComputingNow(calculated, Thread.currentThread().id) // only current thread may see the value
_postCompute!!(key, calculated, data)
calculated
} catch (e: Throwable) {
value = ExceptionWasThrownDuringValueComputation(e)
throw e
}
_calculate = null
_postCompute = null
value = calculatedValue
return calculatedValue
}
is ExceptionWasThrownDuringValueComputation -> {
throw stateSnapshot.error
}
else -> {
return value as VALUE
}
}
}
@Suppress("UNCHECKED_CAST")
fun getValueIfComputed(): VALUE? = when (val snapshot = value) {
ValueIsNotComputed -> null
is ValueIsPostComputingNow -> null
is ExceptionWasThrownDuringValueComputation -> throw snapshot.error
else -> value as VALUE
}
private class ValueIsPostComputingNow(val value: Any?, val threadId: Long)
private class ExceptionWasThrownDuringValueComputation(val error: Throwable)
private object ValueIsNotComputed
}
@@ -0,0 +1,137 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.CheckersComponentInternal
import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.ComposedDeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ComposedExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.type.TypeCheckers
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.analysis.collectors.components.*
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.jvm.checkers.JvmDeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.jvm.checkers.JvmExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.jvm.diagnostics.FirJvmDefaultErrorMessages
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.moduleSourceInfo
import org.jetbrains.kotlin.platform.SimplePlatform
import org.jetbrains.kotlin.platform.jvm.JvmPlatform
internal abstract class AbstractFirIdeDiagnosticsCollector(
session: FirSession,
useExtendedCheckers: Boolean,
) : AbstractDiagnosticCollector(
session,
createComponents = { reporter ->
CheckersFactory.createComponents(session, reporter, useExtendedCheckers)
}
)
private object CheckersFactory {
fun createComponents(
session: FirSession,
reporter: DiagnosticReporter,
useExtendedCheckers: Boolean
): List<AbstractDiagnosticCollectorComponent> {
val moduleInfo = session.moduleData.moduleSourceInfo
val platform = moduleInfo.platform.componentPlatforms.first()
installPlatformSpecificErrorMessages(platform)
val declarationCheckers = createDeclarationCheckers(useExtendedCheckers, platform)
val expressionCheckers = createExpressionCheckers(useExtendedCheckers, platform)
val typeCheckers = createTypeCheckers(useExtendedCheckers)
@OptIn(ExperimentalStdlibApi::class)
return buildList {
if (!useExtendedCheckers) {
add(ErrorNodeDiagnosticCollectorComponent(session, reporter))
}
add(DeclarationCheckersDiagnosticComponent(session, reporter, declarationCheckers))
add(ExpressionCheckersDiagnosticComponent(session, reporter, expressionCheckers))
typeCheckers?.let { add(TypeCheckersDiagnosticComponent(session, reporter, it)) }
add(ControlFlowAnalysisDiagnosticComponent(session, reporter, declarationCheckers))
}
}
private fun createDeclarationCheckers(useExtendedCheckers: Boolean, platform: SimplePlatform): DeclarationCheckers {
return if (useExtendedCheckers) {
ExtendedDeclarationCheckers
} else {
createDeclarationCheckers {
add(CommonDeclarationCheckers)
when (platform) {
is JvmPlatform -> add(JvmDeclarationCheckers)
else -> {
}
}
}
}
}
private fun createExpressionCheckers(useExtendedCheckers: Boolean, platform: SimplePlatform): ExpressionCheckers {
return if (useExtendedCheckers) {
ExtendedExpressionCheckers
} else {
createExpressionCheckers {
add(CommonExpressionCheckers)
when (platform) {
is JvmPlatform -> add(JvmExpressionCheckers)
else -> {
}
}
}
}
}
private fun installPlatformSpecificErrorMessages(platform: SimplePlatform) {
when (platform) {
is JvmPlatform -> FirJvmDefaultErrorMessages.installJvmErrorMessages()
else -> {
}
}
}
private fun createTypeCheckers(useExtendedCheckers: Boolean): TypeCheckers? =
if (useExtendedCheckers) null else CommonTypeCheckers
@OptIn(ExperimentalStdlibApi::class)
private inline fun createDeclarationCheckers(
createDeclarationCheckers: MutableList<DeclarationCheckers>.() -> Unit
): DeclarationCheckers =
createDeclarationCheckers(buildList(createDeclarationCheckers))
@OptIn(CheckersComponentInternal::class)
private fun createDeclarationCheckers(declarationCheckers: List<DeclarationCheckers>): DeclarationCheckers {
return when (declarationCheckers.size) {
1 -> declarationCheckers.single()
else -> ComposedDeclarationCheckers().apply {
declarationCheckers.forEach(::register)
}
}
}
@OptIn(ExperimentalStdlibApi::class)
private inline fun createExpressionCheckers(
createExpressionCheckers: MutableList<ExpressionCheckers>.() -> Unit
): ExpressionCheckers = createExpressionCheckers(buildList(createExpressionCheckers))
@OptIn(CheckersComponentInternal::class)
private fun createExpressionCheckers(expressionCheckers: List<ExpressionCheckers>): ExpressionCheckers {
return when (expressionCheckers.size) {
1 -> expressionCheckers.single()
else -> ComposedExpressionCheckers().apply {
expressionCheckers.forEach(::register)
}
}
}
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
abstract class BeforeElementDiagnosticCollectionHandler: FirSessionComponent {
open fun beforeCollectingForElement(element: FirElement) {}
open fun beforeGoingNestedDeclaration(declaration: FirDeclaration, context: CheckerContext) {}
}
val FirSession.beforeElementDiagnosticCollectionHandler: BeforeElementDiagnosticCollectionHandler? by FirSession.nullableSessionComponentAccessor()
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.FileStructureCache
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
internal class DiagnosticsCollector(
private val fileStructureCache: FileStructureCache,
private val cache: ModuleFileCache,
) {
fun getDiagnosticsFor(element: KtElement, filter: DiagnosticCheckerFilter): List<FirPsiDiagnostic> {
val fileStructure = fileStructureCache.getFileStructure(element.containingKtFile, cache)
val structureElement = fileStructure.getStructureElementFor(element)
val diagnostics = structureElement.diagnostics
return diagnostics.diagnosticsFor(filter, element)
}
fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic> {
val fileStructure = fileStructureCache.getFileStructure(ktFile, cache)
return fileStructure.getAllDiagnosticsForFile(filter)
}
}
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
internal class FileStructureElementDiagnosticList(
private val map: Map<PsiElement, List<FirPsiDiagnostic>>
) {
fun diagnosticsFor(element: PsiElement): List<FirPsiDiagnostic> = map[element] ?: emptyList()
inline fun forEach(action: (List<FirPsiDiagnostic>) -> Unit) = map.values.forEach(action)
}
@@ -0,0 +1,122 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.components.AbstractDiagnosticCollectorComponent
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.SessionHolderImpl
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.fir.PersistenceContextCollector
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.fir.PersistentCheckerContextFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LockProvider
internal abstract class FileStructureElementDiagnosticRetriever {
abstract fun retrieve(
firFile: FirFile,
collector: FileStructureElementDiagnosticsCollector,
lockProvider: LockProvider<FirFile>
): FileStructureElementDiagnosticList
}
internal class SingleNonLocalDeclarationDiagnosticRetriever(
private val structureElementDeclaration: FirDeclaration
) : FileStructureElementDiagnosticRetriever() {
override fun retrieve(
firFile: FirFile,
collector: FileStructureElementDiagnosticsCollector,
lockProvider: LockProvider<FirFile>
): FileStructureElementDiagnosticList {
val sessionHolder = SessionHolderImpl.createWithEmptyScopeSession(firFile.moduleData.session)
val context = lockProvider.withWriteLock(firFile) {
PersistenceContextCollector.collectContext(sessionHolder, firFile, structureElementDeclaration)
}
return collector.collectForStructureElement(structureElementDeclaration) { components ->
Visitor(structureElementDeclaration, context, components)
}
}
private class Visitor(
private val structureElementDeclaration: FirDeclaration,
context: CheckerContext,
components: List<AbstractDiagnosticCollectorComponent>
) : FirIdeDiagnosticVisitor(context, components) {
private var insideAlwaysVisitableDeclarations = 0
override fun shouldVisitDeclaration(declaration: FirDeclaration): Boolean {
if (declaration.shouldVisitWithNestedDeclarations()) {
insideAlwaysVisitableDeclarations++
}
if (insideAlwaysVisitableDeclarations > 0) {
return true
}
@Suppress("IntroduceWhenSubject")
return when {
structureElementDeclaration !is FirRegularClass -> true
structureElementDeclaration == declaration -> true
else -> false
}
}
private fun FirDeclaration.shouldVisitWithNestedDeclarations(): Boolean {
if (shouldDiagnosticsAlwaysBeCheckedOn(this)) return true
return when (this) {
is FirAnonymousInitializer -> true
is FirEnumEntry -> true
is FirValueParameter -> true
is FirConstructor -> isPrimary
else -> false
}
}
override fun onDeclarationExit(declaration: FirDeclaration) {
if (declaration.shouldVisitWithNestedDeclarations()) {
insideAlwaysVisitableDeclarations--
}
}
}
companion object {
fun shouldDiagnosticsAlwaysBeCheckedOn(firElement: FirElement) = when (firElement.source?.kind) {
FirFakeSourceElementKind.PropertyFromParameter -> true
FirFakeSourceElementKind.ImplicitConstructor -> true
else -> false
}
}
}
internal object FileDiagnosticRetriever : FileStructureElementDiagnosticRetriever() {
override fun retrieve(
firFile: FirFile,
collector: FileStructureElementDiagnosticsCollector,
lockProvider: LockProvider<FirFile>
): FileStructureElementDiagnosticList =
collector.collectForStructureElement(firFile) { components ->
Visitor(firFile, components)
}
private class Visitor(
firFile: FirFile,
components: List<AbstractDiagnosticCollectorComponent>
) : FirIdeDiagnosticVisitor(
PersistentCheckerContextFactory.createEmptyPersistenceCheckerContext(SessionHolderImpl.createWithEmptyScopeSession(firFile.moduleData.session)),
components,
) {
override fun visitFile(file: FirFile, data: Nothing?) {
withAnnotationContainer(file) {
visitWithDeclaration(file) {
file.annotations.forEach { it.accept(this, data) }
file.packageDirective.accept(this, data)
file.imports.forEach { it.accept(this, data) }
// do not visit declarations here
}
}
}
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import com.intellij.psi.PsiElement
import com.intellij.util.SmartList
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LockProvider
internal class FileStructureElementDiagnostics(
private val firFile: FirFile,
private val lockProvider: LockProvider<FirFile>,
private val retriever: FileStructureElementDiagnosticRetriever
) {
private val diagnosticByCommonCheckers: FileStructureElementDiagnosticList by lazy {
retriever.retrieve(firFile, FileStructureElementDiagnosticsCollector.USUAL_COLLECTOR, lockProvider)
}
private val diagnosticByExtendedCheckers: FileStructureElementDiagnosticList by lazy {
retriever.retrieve(firFile, FileStructureElementDiagnosticsCollector.EXTENDED_COLLECTOR, lockProvider)
}
fun diagnosticsFor(filter: DiagnosticCheckerFilter, element: PsiElement): List<FirPsiDiagnostic> =
SmartList<FirPsiDiagnostic>().apply {
if (filter.runCommonCheckers) {
addAll(diagnosticByCommonCheckers.diagnosticsFor(element))
}
if (filter.runExtendedCheckers) {
addAll(diagnosticByExtendedCheckers.diagnosticsFor(element))
}
}
inline fun forEach(filter: DiagnosticCheckerFilter, action: (List<FirPsiDiagnostic>) -> Unit) {
if (filter.runCommonCheckers) {
diagnosticByCommonCheckers.forEach(action)
}
if (filter.runExtendedCheckers) {
diagnosticByExtendedCheckers.forEach(action)
}
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.analysis.collectors.components.AbstractDiagnosticCollectorComponent
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.fir.FirIdeStructureElementDiagnosticsCollector
internal class FileStructureElementDiagnosticsCollector private constructor(private val useExtendedCheckers: Boolean) {
companion object {
val USUAL_COLLECTOR = FileStructureElementDiagnosticsCollector(useExtendedCheckers = false)
val EXTENDED_COLLECTOR = FileStructureElementDiagnosticsCollector(useExtendedCheckers = true)
}
fun collectForStructureElement(
firDeclaration: FirDeclaration,
createVisitor: (components: List<AbstractDiagnosticCollectorComponent>) -> CheckerRunningDiagnosticCollectorVisitor,
): FileStructureElementDiagnosticList {
val reporter = FirIdeDiagnosticReporter()
val collector = FirIdeStructureElementDiagnosticsCollector(
firDeclaration.moduleData.session,
createVisitor,
useExtendedCheckers,
)
collector.collectDiagnostics(firDeclaration, reporter)
return FileStructureElementDiagnosticList(reporter.diagnostics)
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.addValueFor
internal class FirIdeDiagnosticReporter : DiagnosticReporter() {
val diagnostics = mutableMapOf<PsiElement, MutableList<FirPsiDiagnostic>>()
override fun report(diagnostic: FirDiagnostic?, context: CheckerContext) {
if (diagnostic == null) return
if (context.isDiagnosticSuppressed(diagnostic)) return
val psiDiagnostic = when (diagnostic) {
is FirPsiDiagnostic -> diagnostic
is FirLightDiagnostic -> diagnostic.toPsiDiagnostic()
else -> error("Unknown diagnostic type ${diagnostic::class.simpleName}")
}
diagnostics.addValueFor(psiDiagnostic.psiElement, psiDiagnostic)
}
}
private fun FirLightDiagnostic.toPsiDiagnostic(): FirPsiDiagnostic {
val psiSourceElement = element.unwrapToFirPsiSourceElement()
?: error("Diagnostic should be created from PSI in IDE")
@Suppress("UNCHECKED_CAST")
return when (this) {
is FirLightSimpleDiagnostic -> FirPsiSimpleDiagnostic(
psiSourceElement,
severity,
factory,
positioningStrategy
)
is FirLightDiagnosticWithParameters1<*> -> FirPsiDiagnosticWithParameters1(
psiSourceElement,
a,
severity,
factory as FirDiagnosticFactory1<Any?>,
positioningStrategy
)
is FirLightDiagnosticWithParameters2<*, *> -> FirPsiDiagnosticWithParameters2(
psiSourceElement,
a, b,
severity,
factory as FirDiagnosticFactory2<Any?, Any?>,
positioningStrategy
)
is FirLightDiagnosticWithParameters3<*, *, *> -> FirPsiDiagnosticWithParameters3(
psiSourceElement,
a, b, c,
severity,
factory as FirDiagnosticFactory3<Any?, Any?, Any?>,
positioningStrategy
)
is FirLightDiagnosticWithParameters4<*, *, *, *> -> FirPsiDiagnosticWithParameters4(
psiSourceElement,
a, b, c, d,
severity,
factory as FirDiagnosticFactory4<Any?, Any?, Any?, Any?>,
positioningStrategy
)
else -> error("Unknown diagnostic type ${this::class.simpleName}")
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.analysis.collectors.components.AbstractDiagnosticCollectorComponent
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkCanceled
internal open class FirIdeDiagnosticVisitor(
context: CheckerContext,
components: List<AbstractDiagnosticCollectorComponent>,
) : CheckerRunningDiagnosticCollectorVisitor(context, components) {
private val beforeElementDiagnosticCollectionHandler = context.session.beforeElementDiagnosticCollectionHandler
override fun visitNestedElements(element: FirElement) {
if (element is FirDeclaration) {
beforeElementDiagnosticCollectionHandler?.beforeGoingNestedDeclaration(element, context)
}
super.visitNestedElements(element)
}
override fun checkElement(element: FirElement) {
beforeElementDiagnosticCollectionHandler?.beforeCollectingForElement(element)
components.forEach {
checkCanceled()
element.accept(it, context)
}
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.fir
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.analysis.low.level.api.fir.ContextByDesignationCollector
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignation
private class ContextCollectingDiagnosticCollectorVisitor private constructor(
sessionHolder: SessionHolder,
designation: FirDeclarationDesignationWithFile,
) : AbstractDiagnosticCollectorVisitor(
PersistentCheckerContextFactory.createEmptyPersistenceCheckerContext(sessionHolder)
) {
private val contextCollector = object : ContextByDesignationCollector<CheckerContext>(designation) {
override fun getCurrentContext(): CheckerContext = context
override fun goToNestedDeclaration(declaration: FirDeclaration) {
declaration.accept(this@ContextCollectingDiagnosticCollectorVisitor, null)
}
}
override fun visitNestedElements(element: FirElement) {
if (element is FirDeclaration) {
contextCollector.nextStep()
} else {
element.accept(this, null)
}
}
override fun checkElement(element: FirElement) {}
companion object {
fun collect(sessionHolder: SessionHolder, designation: FirDeclarationDesignationWithFile): CheckerContext {
val visitor = ContextCollectingDiagnosticCollectorVisitor(sessionHolder, designation)
designation.firFile.accept(visitor, null)
return visitor.contextCollector.getCollectedContext()
}
}
}
internal object PersistenceContextCollector {
fun collectContext(
sessionHolder: SessionHolder,
firFile: FirFile,
declaration: FirDeclaration,
): CheckerContext {
val isLocal = when (declaration) {
is FirClassLikeDeclaration -> declaration.symbol.classId.isLocal
is FirCallableDeclaration -> declaration.symbol.callableId.isLocal
else -> error("Unsupported declaration ${declaration.renderWithType()}")
}
require(!isLocal) {
"Cannot collect context for local declaration ${declaration.renderWithType()}"
}
val designation = declaration.collectDesignation(firFile)
return ContextCollectingDiagnosticCollectorVisitor.collect(sessionHolder, designation)
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.fir
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.analysis.collectors.components.AbstractDiagnosticCollectorComponent
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.AbstractFirIdeDiagnosticsCollector
internal class FirIdeStructureElementDiagnosticsCollector(
session: FirSession,
private val doCreateVisitor: (components: List<AbstractDiagnosticCollectorComponent>) -> CheckerRunningDiagnosticCollectorVisitor,
useExtendedCheckers: Boolean,
) : AbstractFirIdeDiagnosticsCollector(
session,
useExtendedCheckers,
) {
override fun createVisitor(components: List<AbstractDiagnosticCollectorComponent>): CheckerRunningDiagnosticCollectorVisitor {
return doCreateVisitor(components)
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.fir
import org.jetbrains.kotlin.fir.analysis.checkers.context.PersistentCheckerContext
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.ImplicitBodyResolveComputationSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.createReturnTypeCalculatorForIDE
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirIdeDesignatedImpliciteTypesBodyResolveTransformerForReturnTypeCalculator
internal object PersistentCheckerContextFactory {
fun createEmptyPersistenceCheckerContext(sessionHolder: SessionHolder): PersistentCheckerContext {
val returnTypeCalculator = createReturnTypeCalculatorForIDE(
sessionHolder.session,
ScopeSession(),
ImplicitBodyResolveComputationSession(),
::FirIdeDesignatedImpliciteTypesBodyResolveTransformerForReturnTypeCalculator
)
return PersistentCheckerContext(sessionHolder, returnTypeCalculator)
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getElementTextInContext
import org.jetbrains.kotlin.psi.KtElement
class DuplicatedFirSourceElementsException(
existingFir: FirElement,
newFir: FirElement,
psi: KtElement
) : IllegalStateException() {
override val message: String? = """|The PSI element should be used only once as a real PSI source of FirElement,
|the elements ${if (existingFir.source === newFir.source) "HAVE" else "DON'T HAVE"} the same instances of source elements
|
|existing FIR element is $existingFir with text:
|${existingFir.render().trim()}
|
|new FIR element is $newFir with text:
| ${newFir.render().trim()}
|
|PSI element is $psi with text in context:
|${psi.getElementTextInContext()}""".trimMargin()
companion object {
// The are some cases which are still generates FIR elements with duplicated source elements
// Then such case is met, it's better to be fixed
// but exception reporting can be easily disabled by setting this to false
var IS_ENABLED = false
}
}
@@ -0,0 +1,153 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder
import com.intellij.psi.PsiElement
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.ThreadSafe
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.FileStructureCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.FileStructureElement
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.declarationCanBeLazilyResolved
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getElementTextInContext
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.isNonAnonymousClassOrObject
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi2ir.deparenthesize
/**
* Maps [KtElement] to [FirElement]
* Stateless, caches everything into [ModuleFileCache] & [FileStructureCache] passed into the function
*/
@ThreadSafe
internal class FirElementBuilder {
fun getPsiAsFirElementSource(element: KtElement): KtElement? {
val deparenthesized = if (element is KtPropertyDelegate) element.deparenthesize() else element
return when {
deparenthesized is KtParenthesizedExpression -> deparenthesized.deparenthesize()
deparenthesized is KtPropertyDelegate -> deparenthesized.expression ?: element
deparenthesized is KtQualifiedExpression && deparenthesized.selectorExpression is KtCallExpression -> {
/*
KtQualifiedExpression with KtCallExpression in selector transformed in FIR to FirFunctionCall expression
Which will have a receiver as qualifier
*/
deparenthesized.selectorExpression ?: error("Incomplete code:\n${element.getElementTextInContext()}")
}
deparenthesized is KtValueArgument -> {
// null will be return in case of invalid KtValueArgument
deparenthesized.getArgumentExpression()
}
deparenthesized is KtObjectLiteralExpression -> deparenthesized.objectDeclaration
deparenthesized is KtStringTemplateEntryWithExpression -> deparenthesized.expression
deparenthesized is KtUserType && deparenthesized.parent is KtNullableType -> deparenthesized.parent as KtNullableType
else -> deparenthesized
}
}
fun doKtElementHasCorrespondingFirElement(ktElement: KtElement): Boolean = when (ktElement) {
is KtImportList -> false
else -> true
}
fun getOrBuildFirFor(
element: KtElement,
firFileBuilder: FirFileBuilder,
moduleFileCache: ModuleFileCache,
fileStructureCache: FileStructureCache,
firLazyDeclarationResolver: FirLazyDeclarationResolver,
state: FirModuleResolveState,
): FirElement? = when (element) {
is KtFile -> getOrBuildFirForKtFile(element, firFileBuilder, moduleFileCache, firLazyDeclarationResolver)
else -> getOrBuildFirForNonKtFileElement(element, fileStructureCache, moduleFileCache, state)
}
private fun getOrBuildFirForKtFile(
ktFile: KtFile,
firFileBuilder: FirFileBuilder,
moduleFileCache: ModuleFileCache,
firLazyDeclarationResolver: FirLazyDeclarationResolver
): FirFile {
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache, preferLazyBodies = false)
firLazyDeclarationResolver.lazyResolveFileDeclaration(
firFile = firFile,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.BODY_RESOLVE,
scopeSession = ScopeSession(),
checkPCE = true
)
return firFile
}
private fun getOrBuildFirForNonKtFileElement(
element: KtElement,
fileStructureCache: FileStructureCache,
moduleFileCache: ModuleFileCache,
state: FirModuleResolveState,
): FirElement? {
require(element !is KtFile)
if (!doKtElementHasCorrespondingFirElement(element)) {
return null
}
val firFile = element.containingKtFile
val fileStructure = fileStructureCache.getFileStructure(firFile, moduleFileCache)
val mappings = fileStructure.getStructureElementFor(element).mappings
val psi = getPsiAsFirElementSource(element) ?: return null
return mappings.getFirOfClosestParent(psi, state)
?: state.getOrBuildFirFile(firFile)
}
@TestOnly
fun getStructureElementFor(
element: KtElement,
moduleFileCache: ModuleFileCache,
fileStructureCache: FileStructureCache,
): FileStructureElement {
val fileStructure = fileStructureCache.getFileStructure(element.containingKtFile, moduleFileCache)
return fileStructure.getStructureElementFor(element)
}
}
// TODO: simplify
internal inline fun PsiElement.getNonLocalContainingOrThisDeclaration(predicate: (KtDeclaration) -> Boolean = { true }): KtNamedDeclaration? {
var container: PsiElement? = this
while (container != null && container !is KtFile) {
if (container is KtNamedDeclaration
&& (container.isNonAnonymousClassOrObject() || container is KtDeclarationWithBody || container is KtProperty || container is KtTypeAlias)
&& container !is KtPrimaryConstructor
&& declarationCanBeLazilyResolved(container)
&& container !is KtEnumEntry
&& container !is KtFunctionLiteral
&& container.containingClassOrObject !is KtEnumEntry
&& predicate(container)
) {
return container
}
container = container.parent
}
return null
}
fun PsiElement.getNonLocalContainingInBodyDeclarationWith(): KtNamedDeclaration? =
getNonLocalContainingOrThisDeclaration { declaration ->
when (declaration) {
is KtNamedFunction -> declaration.bodyExpression?.isAncestor(this) == true
is KtProperty -> declaration.initializer?.isAncestor(this) == true ||
declaration.getter?.isAncestor(this) == true ||
declaration.setter?.isAncestor(this) == true
else -> false
}
}
@@ -0,0 +1,107 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDesignatedBodyResolveTransformerForReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.ImplicitBodyResolveComputationSession
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyBodiesCalculator
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
fun FirIdeDesignatedImpliciteTypesBodyResolveTransformerForReturnTypeCalculator(
designation: Iterator<FirElement>,
session: FirSession,
scopeSession: ScopeSession,
implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession,
returnTypeCalculator: ReturnTypeCalculator,
outerBodyResolveContext: BodyResolveContext?,
): FirDesignatedBodyResolveTransformerForReturnTypeCalculator {
val designationList = mutableListOf<FirElement>()
designation.forEachRemaining(designationList::add)
require(designationList.isNotEmpty()) { "Designation should not be empty" }
return FirIdeDesignatedBodyResolveTransformerForReturnTypeCalculatorImpl(
designationList,
session,
scopeSession,
implicitBodyResolveComputationSession,
returnTypeCalculator,
outerBodyResolveContext
)
}
private class FirIdeDesignatedBodyResolveTransformerForReturnTypeCalculatorImpl(
private val designation: List<FirElement>,
session: FirSession,
scopeSession: ScopeSession,
implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession,
returnTypeCalculator: ReturnTypeCalculator,
outerBodyResolveContext: BodyResolveContext?,
) : FirDesignatedBodyResolveTransformerForReturnTypeCalculator(
designation.iterator(),
session,
scopeSession,
implicitBodyResolveComputationSession,
returnTypeCalculator,
outerBodyResolveContext
) {
private val targetDeclaration = designation.last()
private inline fun <D : FirCallableDeclaration> D.processCallable(body: (FirDeclarationDesignation) -> Unit) {
if (this !== targetDeclaration) return
if (resolvePhase < FirResolvePhase.TYPES && returnTypeRef is FirResolvedTypeRef) return
ensureResolved(FirResolvePhase.TYPES)
if (returnTypeRef is FirImplicitTypeRef) {
val declarationList = designation.filterIsInstance<FirDeclaration>()
check(declarationList.isNotEmpty()) { "Invalid empty declaration designation" }
body(FirDeclarationDesignation(declarationList.dropLast(1), this))
}
}
override fun transformSimpleFunction(
simpleFunction: FirSimpleFunction,
data: ResolutionMode
): FirSimpleFunction {
simpleFunction.processCallable {
FirLazyBodiesCalculator.calculateLazyBodiesForFunction(it)
}
return if (simpleFunction.returnTypeRef is FirResolvedTypeRef) {
super.transformSimpleFunction(simpleFunction, data)
} else {
return ResolveTreeBuilder.resolveReturnTypeCalculation(simpleFunction) {
super.transformSimpleFunction(simpleFunction, data)
}
}
}
override fun transformProperty(
property: FirProperty,
data: ResolutionMode
): FirProperty {
property.processCallable {
FirLazyBodiesCalculator.calculateLazyBodyForProperty(it)
}
return if (property.returnTypeRef is FirResolvedTypeRef) {
super.transformProperty(property, data)
} else {
return ResolveTreeBuilder.resolveReturnTypeCalculation(property) {
super.transformProperty(property, data)
}
}
}
}
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDesignatedBodyResolveTransformerForReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.ImplicitBodyResolveComputationSession
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
fun FirIdeEnsureBasedTransformerForReturnTypeCalculator(
designation: Iterator<FirElement>,
session: FirSession,
scopeSession: ScopeSession,
implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession,
returnTypeCalculator: ReturnTypeCalculator,
outerBodyResolveContext: BodyResolveContext?,
): FirDesignatedBodyResolveTransformerForReturnTypeCalculator {
val designationList = mutableListOf<FirElement>()
designation.forEachRemaining(designationList::add)
require(designationList.isNotEmpty()) { "Designation should not be empty" }
return FirIdeEnsureBasedTransformerForReturnTypeCalculatorImpl(
designationList,
session,
scopeSession,
implicitBodyResolveComputationSession,
returnTypeCalculator,
outerBodyResolveContext
)
}
private class FirIdeEnsureBasedTransformerForReturnTypeCalculatorImpl(
designation: List<FirElement>,
session: FirSession,
scopeSession: ScopeSession,
implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession,
returnTypeCalculator: ReturnTypeCalculator,
outerBodyResolveContext: BodyResolveContext?,
) : FirDesignatedBodyResolveTransformerForReturnTypeCalculator(
designation.iterator(),
session,
scopeSession,
implicitBodyResolveComputationSession,
returnTypeCalculator,
outerBodyResolveContext
) {
private val targetDeclaration = designation.last()
private fun <T : FirCallableDeclaration> T.ensureReturnType() {
if (this !== targetDeclaration) return
if (resolvePhase < FirResolvePhase.TYPES && returnTypeRef is FirResolvedTypeRef) return
ensureResolved(FirResolvePhase.TYPES)
if (returnTypeRef is FirImplicitTypeRef) {
ensureResolved(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE)
}
}
override fun transformSimpleFunction(simpleFunction: FirSimpleFunction, data: ResolutionMode): FirSimpleFunction {
simpleFunction.ensureReturnType()
return super.transformSimpleFunction(simpleFunction, data)
}
override fun transformProperty(property: FirProperty, data: ResolutionMode): FirProperty {
property.ensureReturnType()
return super.transformProperty(property, data)
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalDeclaration
import org.jetbrains.kotlin.psi.*
interface FirTowerContextProvider {
fun getClosestAvailableParentContext(ktElement: KtElement): FirTowerDataContext?
}
internal class FileTowerProvider(
private val file: KtFile,
private val context: FirTowerDataContext
) : FirTowerContextProvider {
override fun getClosestAvailableParentContext(ktElement: KtElement): FirTowerDataContext? =
if (file == ktElement.containingKtFile) context else null
}
internal class FirTowerDataContextAllElementsCollector : FirTowerDataContextCollector, FirTowerContextProvider {
private val elementsToContext: MutableMap<KtElement, FirTowerDataContext> = hashMapOf()
override fun addFileContext(file: FirFile, context: FirTowerDataContext) {
val ktFile = file.psi as? KtFile ?: return
elementsToContext[ktFile] = context
}
override fun addStatementContext(statement: FirStatement, context: FirTowerDataContext) {
val closestStatementInBlock = statement.psi?.closestBlockLevelOrInitializerExpression() ?: return
elementsToContext[closestStatementInBlock] = context
}
override fun addDeclarationContext(declaration: FirDeclaration, context: FirTowerDataContext) {
val psi = declaration.psi as? KtElement ?: return
elementsToContext[psi] = context
}
override fun getClosestAvailableParentContext(ktElement: KtElement): FirTowerDataContext? {
var current: PsiElement? = ktElement
while (current != null) {
if (current is KtElement) {
elementsToContext[current]?.let { return it }
}
if (current is KtDeclaration) {
val originalDeclaration = current.originalDeclaration
originalDeclaration?.let { elementsToContext[it] }?.let { return it }
}
if (current is KtFile) {
break
}
current = current.parent
}
return null
}
}
private tailrec fun PsiElement.closestBlockLevelOrInitializerExpression(): KtExpression? =
when {
this is KtExpression && (parent is KtBlockExpression || parent is KtDeclarationWithInitializer) -> this
else -> parent?.closestBlockLevelOrInitializerExpression()
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.builder.BodyBuildingMode
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.ThreadSafe
import org.jetbrains.kotlin.psi.KtFile
/**
* Responsible for building [FirFile] by [KtFile]
* Stateless, all caches are stored in [ModuleFileCache] passed into corresponding functions
*/
@ThreadSafe
internal class FirFileBuilder(
private val scopeProvider: FirScopeProvider,
val firPhaseRunner: FirPhaseRunner
) {
/**
* Builds a [FirFile] by given [ktFile] and records it's parenting info if it not present in [cache]
* [FirFile] building a happens at most once per each [KtFile]
*/
fun buildRawFirFileWithCaching(
ktFile: KtFile,
cache: ModuleFileCache,
preferLazyBodies: Boolean
): FirFile = cache.fileCached(ktFile) {
RawFirBuilder(
cache.session,
scopeProvider,
psiMode = PsiHandlingMode.IDE,
bodyBuildingMode = BodyBuildingMode.lazyBodies(preferLazyBodies)
).buildFirFile(ktFile)
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.PrivateForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.lockWithPCECheck
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
/**
* Keyed locks provider.
* !!! We temporary remove its correct implementation to fix deadlocks problem. Do not use this until this comment is present
*/
internal class LockProvider<KEY> {
//We temporary disable multi-locks to fix deadlocks problem
private val globalLock = ReentrantLock()
@OptIn(PrivateForInline::class)
inline fun <R> withWriteLock(@Suppress("UNUSED_PARAMETER") key: KEY, action: () -> R): R {
val startTime = System.currentTimeMillis()
return globalLock.withLock { ResolveTreeBuilder.lockNode(startTime, action) }
}
@OptIn(PrivateForInline::class)
inline fun <R> withWriteLockPCECheck(@Suppress("UNUSED_PARAMETER") key: KEY, lockingIntervalMs: Long, action: () -> R): R {
val startTime = System.currentTimeMillis()
return globalLock.lockWithPCECheck(lockingIntervalMs) { ResolveTreeBuilder.lockNode(startTime, action) }
}
}
/**
* Runs [resolve] function (which is considered to do some resolve on [firFile]) under a lock for [firFile]
*/
internal inline fun <R> LockProvider<FirFile>.runCustomResolveUnderLock(
firFile: FirFile,
checkPCE: Boolean,
body: () -> R
): R {
return if (checkPCE) {
withWriteLockPCECheck(key = firFile, lockingIntervalMs = 50L, body)
} else {
withWriteLock(key = firFile, action = body)
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder
import com.intellij.concurrency.ConcurrentCollectionFactory
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.ThreadSafe
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtFile
import java.util.*
import java.util.concurrent.ConcurrentHashMap
/**
* Caches mapping [KtFile] -> [FirFile] of module [moduleInfo]
*/
@ThreadSafe
internal abstract class ModuleFileCache {
abstract val session: FirSession
/**
* Maps [ClassId] to corresponding classifiers
* If classifier with required [ClassId] is not found in given module then map contains [Optional.EMPTY]
*/
abstract val classifierByClassId: ConcurrentHashMap<ClassId, Optional<FirClassLikeDeclaration>>
/**
* Maps [CallableId] to corresponding callable
* If callable with required [CallableId]] is not found in given module then map contains emptyList
*/
abstract val callableByCallableId: ConcurrentHashMap<CallableId, List<FirCallableSymbol<*>>>
/**
* @return [FirFile] by [file] if it was previously built or runs [createValue] otherwise
* The [createValue] is run under the lock so [createValue] is executed at most once for each [KtFile]
*/
abstract fun fileCached(file: KtFile, createValue: () -> FirFile): FirFile
abstract fun getContainerFirFile(declaration: FirDeclaration): FirFile?
abstract fun getCachedFirFile(ktFile: KtFile): FirFile?
abstract val firFileLockProvider: LockProvider<FirFile>
}
internal class ModuleFileCacheImpl(override val session: FirSession) : ModuleFileCache() {
private val ktFileToFirFile = ConcurrentCollectionFactory.createConcurrentIdentityMap<KtFile, FirFile>()
override val classifierByClassId: ConcurrentHashMap<ClassId, Optional<FirClassLikeDeclaration>> = ConcurrentHashMap()
override val callableByCallableId: ConcurrentHashMap<CallableId, List<FirCallableSymbol<*>>> = ConcurrentHashMap()
override fun fileCached(file: KtFile, createValue: () -> FirFile): FirFile =
ktFileToFirFile.computeIfAbsent(file) { createValue() }
override fun getCachedFirFile(ktFile: KtFile): FirFile? = ktFileToFirFile[ktFile]
override fun getContainerFirFile(declaration: FirDeclaration): FirFile? {
val ktFile = declaration.psi?.containingFile as? KtFile ?: return null
return getCachedFirFile(ktFile)
}
override val firFileLockProvider: LockProvider<FirFile> = LockProvider()
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LockProvider
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
internal object FileElementFactory {
/**
* should be consistent with [isReanalyzableContainer]
*/
fun createFileStructureElement(
firDeclaration: FirDeclaration,
ktDeclaration: KtDeclaration,
firFile: FirFile,
firFileLockProvider: LockProvider<FirFile>,
): FileStructureElement = when {
ktDeclaration is KtNamedFunction && ktDeclaration.isReanalyzableContainer() -> ReanalyzableFunctionStructureElement(
firFile,
ktDeclaration,
(firDeclaration as FirSimpleFunction).symbol,
ktDeclaration.modificationStamp,
firFileLockProvider,
)
ktDeclaration is KtProperty && ktDeclaration.isReanalyzableContainer() -> ReanalyzablePropertyStructureElement(
firFile,
ktDeclaration,
(firDeclaration as FirProperty).symbol,
ktDeclaration.modificationStamp,
firFileLockProvider,
)
else -> NonReanalyzableDeclarationStructureElement(
firFile,
firDeclaration,
ktDeclaration,
firFileLockProvider,
)
}
}
/**
* should be consistent with [createFileStructureElement]
*/
//TODO make internal
fun isReanalyzableContainer(
ktDeclaration: KtDeclaration,
): Boolean = when (ktDeclaration) {
is KtNamedFunction -> ktDeclaration.isReanalyzableContainer()
is KtProperty -> ktDeclaration.isReanalyzableContainer()
else -> false
}
private fun KtNamedFunction.isReanalyzableContainer() =
name != null && hasExplicitTypeOrUnit
private fun KtProperty.isReanalyzableContainer() =
name != null && typeReference != null
private val KtNamedFunction.hasExplicitTypeOrUnit
get() = hasBlockBody() || typeReference != null
@@ -0,0 +1,153 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firIdeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.findSourceNonLocalFirDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getElementTextInContext
import org.jetbrains.kotlin.psi.*
import java.util.concurrent.ConcurrentHashMap
internal class FileStructure private constructor(
private val ktFile: KtFile,
private val firFile: FirFile,
private val firLazyDeclarationResolver: FirLazyDeclarationResolver,
private val firFileBuilder: FirFileBuilder,
private val moduleFileCache: ModuleFileCache,
) {
companion object {
fun build(
ktFile: KtFile,
firLazyDeclarationResolver: FirLazyDeclarationResolver,
firFileBuilder: FirFileBuilder,
moduleFileCache: ModuleFileCache,
): FileStructure {
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache, preferLazyBodies = false)
return FileStructure(ktFile, firFile, firLazyDeclarationResolver, firFileBuilder, moduleFileCache)
}
}
private val firIdeProvider = firFile.moduleData.session.firIdeProvider
private val structureElements = ConcurrentHashMap<KtAnnotated, FileStructureElement>()
fun getStructureElementFor(element: KtElement): FileStructureElement {
val container: KtAnnotated = element.getNonLocalContainingOrThisDeclaration() ?: element.containingKtFile
return getStructureElementForDeclaration(container)
}
private fun getStructureElementForDeclaration(declaration: KtAnnotated): FileStructureElement {
@Suppress("CANNOT_CHECK_FOR_ERASED")
val structureElement = structureElements.compute(declaration) { _, structureElement ->
when {
structureElement == null -> createStructureElement(declaration)
structureElement is ReanalyzableStructureElement<KtDeclaration, *> && !structureElement.isUpToDate() -> {
structureElement.reanalyze(
newKtDeclaration = declaration as KtDeclaration,
cache = moduleFileCache,
firLazyDeclarationResolver = firLazyDeclarationResolver,
firIdeProvider = firIdeProvider,
)
}
else -> structureElement
}
}
return structureElement
?: error("FileStructureElement for was not defined for \n${declaration.getElementTextInContext()}")
}
@OptIn(ExperimentalStdlibApi::class)
fun getAllDiagnosticsForFile(diagnosticCheckerFilter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic> {
val structureElements = getAllStructureElements()
return buildList {
collectDiagnosticsFromStructureElements(structureElements, diagnosticCheckerFilter)
}
}
private fun MutableCollection<FirPsiDiagnostic>.collectDiagnosticsFromStructureElements(
structureElements: Collection<FileStructureElement>,
diagnosticCheckerFilter: DiagnosticCheckerFilter
) {
structureElements.forEach { structureElement ->
structureElement.diagnostics.forEach(diagnosticCheckerFilter) { diagnostics ->
addAll(diagnostics)
}
}
}
fun getAllStructureElements(): Collection<FileStructureElement> {
val structureElements = mutableSetOf(getStructureElementFor(ktFile))
ktFile.accept(object : KtVisitorVoid() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
override fun visitDeclaration(dcl: KtDeclaration) {
val structureElement = getStructureElementFor(dcl)
structureElements += structureElement
if (structureElement !is ReanalyzableStructureElement<*, *>) {
dcl.acceptChildren(this)
}
}
})
return structureElements
}
private fun createDeclarationStructure(declaration: KtDeclaration): FileStructureElement {
val firDeclaration = declaration.findSourceNonLocalFirDeclaration(
firFileBuilder,
firIdeProvider.symbolProvider,
moduleFileCache,
firFile
)
val resolvedDeclaration = firLazyDeclarationResolver.lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
moduleFileCache = moduleFileCache,
scopeSession = ScopeSession(),
toPhase = FirResolvePhase.BODY_RESOLVE,
checkPCE = true,
)
return FileElementFactory.createFileStructureElement(
firDeclaration = resolvedDeclaration,
ktDeclaration = declaration,
firFile = firFile,
firFileLockProvider = moduleFileCache.firFileLockProvider
)
}
private fun createStructureElement(container: KtAnnotated): FileStructureElement = when (container) {
is KtFile -> {
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache, preferLazyBodies = true)
firLazyDeclarationResolver.resolveFileAnnotations(
firFile = firFile,
annotations = firFile.annotations,
moduleFileCache = moduleFileCache,
scopeSession = ScopeSession(),
checkPCE = true
)
RootStructureElement(
firFile,
container,
moduleFileCache.firFileLockProvider,
)
}
is KtDeclaration -> createDeclarationStructure(container)
else -> error("Invalid container $container")
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.psi.KtFile
import java.util.concurrent.ConcurrentHashMap
/**
* Belongs to a [org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState]
*/
internal class FileStructureCache(
private val fileBuilder: FirFileBuilder,
private val firLazyDeclarationResolver: FirLazyDeclarationResolver,
) {
private val cache = ConcurrentHashMap<KtFile, FileStructure>()
fun getFileStructure(ktFile: KtFile, moduleFileCache: ModuleFileCache): FileStructure = cache.computeIfAbsent(ktFile) {
FileStructure.build(ktFile, firLazyDeclarationResolver, fileBuilder, moduleFileCache)
}
}
@@ -0,0 +1,277 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LowLevelFirApiFacadeForResolveOnAir
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileDiagnosticRetriever
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileStructureElementDiagnostics
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.SingleNonLocalDeclarationDiagnosticRetriever
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LockProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.RawFirNonLocalDeclarationBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.declarationCanBeLazilyResolved
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.FirIdeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firIdeProvider
import org.jetbrains.kotlin.psi.*
import java.util.concurrent.ConcurrentHashMap
internal sealed class FileStructureElement(val firFile: FirFile, protected val lockProvider: LockProvider<FirFile>) {
abstract val psi: KtAnnotated
abstract val mappings: KtToFirMapping
abstract val diagnostics: FileStructureElementDiagnostics
}
internal class KtToFirMapping(firElement: FirElement, recorder: FirElementsRecorder) {
private val mapping = FirElementsRecorder.recordElementsFrom(firElement, recorder)
private val userTypeMapping = ConcurrentHashMap<KtUserType, FirElement>()
fun getElement(ktElement: KtElement, state: FirModuleResolveState): FirElement? {
mapping[ktElement]?.let { return it }
val userType = when (ktElement) {
is KtUserType -> ktElement
is KtNameReferenceExpression -> ktElement as? KtUserType
else -> null
} ?: return null
//This is for not inner KtUserType
if (userType.parent is KtTypeReference) return null
return userTypeMapping.getOrPut(userType) {
val typeReference = KtPsiFactory(ktElement.project).createType(userType.text)
LowLevelFirApiFacadeForResolveOnAir.onAirResolveTypeInPlace(ktElement, typeReference, state)
}
}
fun getFirOfClosestParent(element: KtElement, state: FirModuleResolveState): FirElement? {
var current: PsiElement? = element
while (current != null && current !is KtFile) {
if (current is KtElement) {
getElement(current, state)?.let { return it }
}
current = current.parent
}
return null
}
}
internal sealed class ReanalyzableStructureElement<KT : KtDeclaration, S : FirBasedSymbol<*>>(
firFile: FirFile,
val firSymbol: S,
lockProvider: LockProvider<FirFile>,
) : FileStructureElement(firFile, lockProvider) {
abstract override val psi: KtDeclaration
abstract val timestamp: Long
/**
* Creates new declaration by [newKtDeclaration] which will serve as replacement of [firSymbol]
* Also, modify [firFile] & replace old version of declaration with a new one
*/
abstract fun reanalyze(
newKtDeclaration: KT,
cache: ModuleFileCache,
firLazyDeclarationResolver: FirLazyDeclarationResolver,
firIdeProvider: FirIdeProvider,
): ReanalyzableStructureElement<KT, S>
fun isUpToDate(): Boolean = psi.getModificationStamp() == timestamp
override val diagnostics = FileStructureElementDiagnostics(
firFile,
lockProvider,
SingleNonLocalDeclarationDiagnosticRetriever(firSymbol.fir)
)
companion object {
val recorder = FirElementsRecorder()
}
}
internal class ReanalyzableFunctionStructureElement(
firFile: FirFile,
override val psi: KtNamedFunction,
firSymbol: FirFunctionSymbol<*>,
override val timestamp: Long,
lockProvider: LockProvider<FirFile>,
) : ReanalyzableStructureElement<KtNamedFunction, FirFunctionSymbol<*>>(firFile, firSymbol, lockProvider) {
override val mappings = KtToFirMapping(firSymbol.fir, recorder)
override fun reanalyze(
newKtDeclaration: KtNamedFunction,
cache: ModuleFileCache,
firLazyDeclarationResolver: FirLazyDeclarationResolver,
firIdeProvider: FirIdeProvider,
): ReanalyzableFunctionStructureElement {
val originalFunction = firSymbol.fir as FirSimpleFunction
val designation = originalFunction.collectDesignation()
val temporaryFunction = RawFirNonLocalDeclarationBuilder.buildWithFunctionSymbolRebind(
session = originalFunction.moduleData.session,
scopeProvider = originalFunction.moduleData.session.firIdeProvider.kotlinScopeProvider,
designation = designation,
rootNonLocalDeclaration = newKtDeclaration,
) as FirSimpleFunction
return cache.firFileLockProvider.withWriteLock(firFile) {
val upgradedPhase = minOf(originalFunction.resolvePhase, FirResolvePhase.DECLARATIONS)
with(originalFunction) {
replaceBody(temporaryFunction.body)
replaceContractDescription(temporaryFunction.contractDescription)
replaceResolvePhase(upgradedPhase)
}
designation.toSequence(includeTarget = true).forEach {
it.replaceResolvePhase(minOf(it.resolvePhase, upgradedPhase))
}
val resolvedDeclaration = firLazyDeclarationResolver.lazyResolveDeclaration(
firDeclarationToResolve = originalFunction,
moduleFileCache = cache,
scopeSession = ScopeSession(),
toPhase = FirResolvePhase.BODY_RESOLVE,
checkPCE = true,
)
check(resolvedDeclaration === originalFunction) {
"Reanalysed declaration not expected to be updated"
}
ReanalyzableFunctionStructureElement(
firFile,
newKtDeclaration,
originalFunction.symbol,
newKtDeclaration.modificationStamp,
lockProvider,
)
}
}
}
internal class ReanalyzablePropertyStructureElement(
firFile: FirFile,
override val psi: KtProperty,
firSymbol: FirPropertySymbol,
override val timestamp: Long,
lockProvider: LockProvider<FirFile>,
) : ReanalyzableStructureElement<KtProperty, FirPropertySymbol>(firFile, firSymbol, lockProvider) {
override val mappings = KtToFirMapping(firSymbol.fir, recorder)
override fun reanalyze(
newKtDeclaration: KtProperty,
cache: ModuleFileCache,
firLazyDeclarationResolver: FirLazyDeclarationResolver,
firIdeProvider: FirIdeProvider,
): ReanalyzablePropertyStructureElement {
val originalProperty = firSymbol.fir
val designation = originalProperty.collectDesignation()
val temporaryProperty = RawFirNonLocalDeclarationBuilder.buildWithFunctionSymbolRebind(
session = originalProperty.moduleData.session,
scopeProvider = originalProperty.moduleData.session.firIdeProvider.kotlinScopeProvider,
designation = designation,
rootNonLocalDeclaration = newKtDeclaration,
) as FirProperty
return cache.firFileLockProvider.withWriteLock(firFile) {
val getterPhase = originalProperty.getter?.resolvePhase ?: originalProperty.resolvePhase
val setterPhase = originalProperty.setter?.resolvePhase ?: originalProperty.resolvePhase
val upgradedPhase = minOf(originalProperty.resolvePhase, getterPhase, setterPhase, FirResolvePhase.DECLARATIONS)
with(originalProperty) {
getter?.replaceBody(temporaryProperty.getter?.body)
setter?.replaceBody(temporaryProperty.setter?.body)
replaceInitializer(temporaryProperty.initializer)
getter?.replaceResolvePhase(upgradedPhase)
setter?.replaceResolvePhase(upgradedPhase)
replaceResolvePhase(upgradedPhase)
replaceBodyResolveState(FirPropertyBodyResolveState.NOTHING_RESOLVED)
}
val resolvedDeclaration = firLazyDeclarationResolver.lazyResolveDeclaration(
firDeclarationToResolve = originalProperty,
moduleFileCache = cache,
scopeSession = ScopeSession(),
toPhase = FirResolvePhase.BODY_RESOLVE,
checkPCE = true,
)
check(resolvedDeclaration === originalProperty) {
"Reanalysed declaration not expected to be updated"
}
ReanalyzablePropertyStructureElement(
firFile,
newKtDeclaration,
originalProperty.symbol,
newKtDeclaration.modificationStamp,
lockProvider,
)
}
}
}
internal class NonReanalyzableDeclarationStructureElement(
firFile: FirFile,
val fir: FirDeclaration,
override val psi: KtDeclaration,
lockProvider: LockProvider<FirFile>,
) : FileStructureElement(firFile, lockProvider) {
override val mappings = KtToFirMapping(fir, recorder)
override val diagnostics = FileStructureElementDiagnostics(firFile, lockProvider, SingleNonLocalDeclarationDiagnosticRetriever(fir))
companion object {
private val recorder = object : FirElementsRecorder() {
override fun visitProperty(property: FirProperty, data: MutableMap<KtElement, FirElement>) {
val psi = property.psi as? KtProperty ?: return super.visitProperty(property, data)
if (!isReanalyzableContainer(psi) || !declarationCanBeLazilyResolved(psi)) {
super.visitProperty(property, data)
}
}
override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: MutableMap<KtElement, FirElement>) {
val psi = simpleFunction.psi as? KtNamedFunction ?: return super.visitSimpleFunction(simpleFunction, data)
if (!isReanalyzableContainer(psi) || !declarationCanBeLazilyResolved(psi)) {
super.visitSimpleFunction(simpleFunction, data)
}
}
}
}
}
internal class RootStructureElement(
firFile: FirFile,
override val psi: KtFile,
lockProvider: LockProvider<FirFile>,
) : FileStructureElement(firFile, lockProvider) {
override val mappings = KtToFirMapping(firFile, recorder)
override val diagnostics = FileStructureElementDiagnostics(firFile, lockProvider, FileDiagnosticRetriever)
companion object {
private val recorder = object : FirElementsRecorder() {
override fun visitElement(element: FirElement, data: MutableMap<KtElement, FirElement>) {
if (element !is FirDeclaration || element is FirFile) {
super.visitElement(element, data)
}
}
}
}
}
@@ -0,0 +1,7 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
@@ -0,0 +1,7 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
@@ -0,0 +1,7 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.declarationCanBeLazilyResolved
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.replaceFirst
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
internal object FileStructureUtil {
fun isStructureElementContainer(ktDeclaration: KtDeclaration): Boolean = when {
ktDeclaration !is KtClassOrObject && ktDeclaration !is KtDeclarationWithBody && ktDeclaration !is KtProperty && ktDeclaration !is KtTypeAlias -> false
ktDeclaration is KtEnumEntry -> false
ktDeclaration.containingClassOrObject is KtEnumEntry -> false
ktDeclaration is KtNamedDeclaration -> !declarationCanBeLazilyResolved(ktDeclaration)
else -> false
}
fun replaceDeclaration(firFile: FirFile, from: FirCallableDeclaration, to: FirCallableDeclaration) {
val declarations = if (from.symbol.callableId.className == null) {
firFile.declarations as MutableList<FirDeclaration>
} else {
val classLikeLookupTag = from.containingClass()
?: error("Class name should not be null for non-top-level & non-local declarations, but was null for\n${from.render()}")
val containingClass = classLikeLookupTag.toSymbol(firFile.moduleData.session)?.fir as FirRegularClass
containingClass.declarations as MutableList<FirDeclaration>
}
declarations.replaceFirst(from, to)
}
inline fun <R> withDeclarationReplaced(
firFile: FirFile,
cache: ModuleFileCache,
from: FirCallableDeclaration,
to: FirCallableDeclaration,
action: () -> R,
): R {
cache.firFileLockProvider.withWriteLock(firFile) { replaceDeclaration(firFile, from, to) }
return try {
action()
} catch (e: Throwable) {
cache.firFileLockProvider.withWriteLock(firFile) { replaceDeclaration(firFile, to, from) }
throw e
}
}
}
@@ -0,0 +1,107 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirRealPsiSourceElement
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.DuplicatedFirSourceElementsException
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.isErrorElement
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtTypeReference
internal open class FirElementsRecorder : FirVisitor<Unit, MutableMap<KtElement, FirElement>>() {
private fun cache(psi: KtElement, fir: FirElement, cache: MutableMap<KtElement, FirElement>) {
val existingFir = cache[psi]
if (existingFir != null && existingFir !== fir) {
when {
existingFir is FirTypeRef && fir is FirTypeRef && psi is KtTypeReference -> {
// FirTypeRefs are often created during resolve
// a lot of them with have the same source
// we want to take the most "resolved one" here
if (fir is FirResolvedTypeRefImpl && existingFir !is FirResolvedTypeRefImpl) {
cache[psi] = fir
}
}
existingFir.isErrorElement && !fir.isErrorElement -> {
// TODO better handle error elements
// but for now just take first non-error one if such exist
cache[psi] = fir
}
existingFir.isErrorElement || fir.isErrorElement -> {
// do nothing and maybe upgrade to a non-error element in the branch above in the future
}
else -> {
if (DuplicatedFirSourceElementsException.IS_ENABLED) {
throw DuplicatedFirSourceElementsException(existingFir, fir, psi)
}
}
}
}
if (existingFir == null) {
cache[psi] = fir
}
}
override fun visitElement(element: FirElement, data: MutableMap<KtElement, FirElement>) {
cacheElement(element, data)
element.acceptChildren(this, data)
}
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: MutableMap<KtElement, FirElement>) {
cacheElement(variableAssignment.lValue, data) // FirReference is not cached by default
visitElement(variableAssignment, data)
}
//@formatter:off
override fun visitReference(reference: FirReference, data: MutableMap<KtElement, FirElement>) {}
override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference, data: MutableMap<KtElement, FirElement>) {}
override fun visitNamedReference(namedReference: FirNamedReference, data: MutableMap<KtElement, FirElement>) {}
override fun visitDelegateFieldReference(delegateFieldReference: FirDelegateFieldReference, data: MutableMap<KtElement, FirElement>) {}
override fun visitBackingFieldReference(backingFieldReference: FirBackingFieldReference, data: MutableMap<KtElement, FirElement>) {}
override fun visitSuperReference(superReference: FirSuperReference, data: MutableMap<KtElement, FirElement>) {}
override fun visitThisReference(thisReference: FirThisReference, data: MutableMap<KtElement, FirElement>) {}
//@formatter:on
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef, data: MutableMap<KtElement, FirElement>) {
super.visitResolvedTypeRef(errorTypeRef, data)
errorTypeRef.delegatedTypeRef?.accept(this, data)
}
override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: MutableMap<KtElement, FirElement>) {
super.visitResolvedTypeRef(resolvedTypeRef, data)
resolvedTypeRef.delegatedTypeRef?.accept(this, data)
}
override fun visitUserTypeRef(userTypeRef: FirUserTypeRef, data: MutableMap<KtElement, FirElement>) {
userTypeRef.acceptChildren(this, data)
}
private fun cacheElement(element: FirElement, cache: MutableMap<KtElement, FirElement>) {
val psi = element.source
?.takeIf {
it.kind == FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess || it is FirRealPsiSourceElement
}.psi as? KtElement
?: return
cache(psi, element, cache)
}
companion object {
@OptIn(ExperimentalStdlibApi::class)
fun recordElementsFrom(firElement: FirElement, recorder: FirElementsRecorder): Map<KtElement, FirElement> =
buildMap { firElement.accept(recorder, this) }
}
}
@@ -0,0 +1,7 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.ide
@@ -0,0 +1,7 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.ide
@@ -0,0 +1,169 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirWrappedDelegateExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirLazyBlock
import org.jetbrains.kotlin.fir.expressions.impl.FirLazyExpression
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firIdeProvider
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtSecondaryConstructor
internal object FirLazyBodiesCalculator {
fun calculateLazyBodiesInside(designation: FirDeclarationDesignation) {
designation.declaration.transform<FirElement, MutableList<FirDeclaration>>(
FirLazyBodiesCalculatorTransformer,
designation.toSequence(includeTarget = true).toMutableList()
)
}
fun calculateLazyBodies(firFile: FirFile) {
firFile.transform<FirElement, MutableList<FirDeclaration>>(FirLazyBodiesCalculatorTransformer, mutableListOf())
}
fun calculateLazyBodiesForFunction(designation: FirDeclarationDesignation) {
val simpleFunction = designation.declaration as FirSimpleFunction
if (simpleFunction.body !is FirLazyBlock) return
val newFunction = RawFirNonLocalDeclarationBuilder.buildWithFunctionSymbolRebind(
session = simpleFunction.moduleData.session,
scopeProvider = simpleFunction.moduleData.session.firIdeProvider.kotlinScopeProvider,
designation = designation,
rootNonLocalDeclaration = simpleFunction.psi as KtNamedFunction,
) as FirSimpleFunction
simpleFunction.apply {
replaceBody(newFunction.body)
replaceContractDescription(newFunction.contractDescription)
}
}
fun calculateLazyBodyForSecondaryConstructor(designation: FirDeclarationDesignation) {
val secondaryConstructor = designation.declaration as FirConstructor
require(!secondaryConstructor.isPrimary)
if (secondaryConstructor.body !is FirLazyBlock) return
val newFunction = RawFirNonLocalDeclarationBuilder.buildWithFunctionSymbolRebind(
session = secondaryConstructor.moduleData.session,
scopeProvider = secondaryConstructor.moduleData.session.firIdeProvider.kotlinScopeProvider,
designation = designation,
rootNonLocalDeclaration = secondaryConstructor.psi as KtSecondaryConstructor,
) as FirSimpleFunction
secondaryConstructor.apply {
replaceBody(newFunction.body)
}
}
fun calculateLazyBodyForProperty(designation: FirDeclarationDesignation) {
val firProperty = designation.declaration as FirProperty
if (!needCalculatingLazyBodyForProperty(firProperty)) return
val newProperty = RawFirNonLocalDeclarationBuilder.buildWithFunctionSymbolRebind(
session = firProperty.moduleData.session,
scopeProvider = firProperty.moduleData.session.firIdeProvider.kotlinScopeProvider,
designation = designation,
rootNonLocalDeclaration = firProperty.psi as KtProperty
) as FirProperty
firProperty.getter?.takeIf { it.body is FirLazyBlock }?.let { getter ->
val newGetter = newProperty.getter!!
getter.replaceBody(newGetter.body)
getter.replaceContractDescription(newGetter.contractDescription)
}
firProperty.setter?.takeIf { it.body is FirLazyBlock }?.let { setter ->
val newSetter = newProperty.setter!!
setter.replaceBody(newSetter.body)
setter.replaceContractDescription(newSetter.contractDescription)
}
if (firProperty.initializer is FirLazyExpression) {
firProperty.replaceInitializer(newProperty.initializer)
}
val delegate = firProperty.delegate as? FirWrappedDelegateExpression
val delegateExpression = delegate?.expression
if (delegateExpression is FirLazyExpression) {
val newDelegate = newProperty.delegate as? FirWrappedDelegateExpression
check(newDelegate != null) { "Invalid replacement delegate" }
delegate.replaceExpression(newDelegate.expression)
val delegateProviderCall = delegate.delegateProvider as? FirFunctionCall
val delegateProviderExplicitReceiver = delegateProviderCall?.explicitReceiver
if (delegateProviderExplicitReceiver is FirLazyExpression) {
val newDelegateProviderExplicitReceiver = (newDelegate.delegateProvider as? FirFunctionCall)?.explicitReceiver
check(newDelegateProviderExplicitReceiver != null) { "Invalid replacement expression" }
delegateProviderCall.replaceExplicitReceiver(newDelegateProviderExplicitReceiver)
}
}
}
fun needCalculatingLazyBodyForProperty(firProperty: FirProperty): Boolean =
firProperty.getter?.body is FirLazyBlock
|| firProperty.setter?.body is FirLazyBlock
|| firProperty.initializer is FirLazyExpression
|| (firProperty.delegate as? FirWrappedDelegateExpression)?.expression is FirLazyExpression
}
private object FirLazyBodiesCalculatorTransformer : FirTransformer<MutableList<FirDeclaration>>() {
override fun transformFile(file: FirFile, data: MutableList<FirDeclaration>): FirFile {
file.declarations.forEach {
it.transformSingle(this, data)
}
return file
}
override fun <E : FirElement> transformElement(element: E, data: MutableList<FirDeclaration>): E {
if (element is FirRegularClass) {
data.add(element)
element.declarations.forEach {
it.transformSingle(this, data)
}
element.transformChildren(this, data)
data.removeLast()
}
return element
}
override fun transformSimpleFunction(
simpleFunction: FirSimpleFunction,
data: MutableList<FirDeclaration>
): FirSimpleFunction {
if (simpleFunction.body is FirLazyBlock) {
val designation = FirDeclarationDesignation(data, simpleFunction)
FirLazyBodiesCalculator.calculateLazyBodiesForFunction(designation)
}
return simpleFunction
}
override fun transformConstructor(
constructor: FirConstructor,
data: MutableList<FirDeclaration>
): FirConstructor {
if (constructor.body is FirLazyBlock) {
val designation = FirDeclarationDesignation(data, constructor)
FirLazyBodiesCalculator.calculateLazyBodyForSecondaryConstructor(designation)
}
return constructor
}
override fun transformProperty(property: FirProperty, data: MutableList<FirDeclaration>): FirProperty {
if (FirLazyBodiesCalculator.needCalculatingLazyBodyForProperty(property)) {
val designation = FirDeclarationDesignation(data, property)
FirLazyBodiesCalculator.calculateLazyBodyForProperty(designation)
}
return property
}
}
@@ -0,0 +1,151 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
enum class ResolveType {
NoResolve,
BodyResolveWithChildren,
CallableBodyResolve,
CallableReturnType,
AnnotationType,
AnnotationsArguments,
ClassSuperTypes,
CallableContracts,
DeclarationStatus,
ValueParametersTypes,
TypeParametersTypes,
// ResolveForMemberScope,
// ResolveForSuperMembers,
}
internal fun <D : FirDeclaration> FirLazyDeclarationResolver.lazyResolveDeclaration(
firDeclaration: D,
moduleFileCache: ModuleFileCache,
toResolveType: ResolveType,
scopeSession: ScopeSession,
checkPCE: Boolean = false,
): D {
return when (toResolveType) {
ResolveType.NoResolve -> return firDeclaration
ResolveType.CallableReturnType -> {
require(firDeclaration is FirCallableDeclaration) {
"CallableReturnType type cannot be applied to ${firDeclaration::class.qualifiedName}"
}
var currentDeclaration = firDeclaration as FirCallableDeclaration
if (currentDeclaration.resolvePhase < FirResolvePhase.TYPES) {
if (currentDeclaration.returnTypeRef !is FirResolvedTypeRef) {
currentDeclaration = lazyResolveDeclaration(
firDeclarationToResolve = currentDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.TYPES,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
}
if (currentDeclaration.returnTypeRef !is FirResolvedTypeRef) {
currentDeclaration = lazyResolveDeclaration(
firDeclarationToResolve = currentDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
check(currentDeclaration.returnTypeRef is FirResolvedTypeRef)
@Suppress("UNCHECKED_CAST")
currentDeclaration as D
}
ResolveType.BodyResolveWithChildren, ResolveType.CallableBodyResolve -> {
require(firDeclaration is FirCallableDeclaration || toResolveType != ResolveType.CallableBodyResolve) {
"BodyResolveWithChildren and CallableBodyResolve types cannot be applied to ${firDeclaration::class.qualifiedName}"
}
lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.BODY_RESOLVE,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
ResolveType.AnnotationType, ResolveType.AnnotationsArguments -> {
if (firDeclaration is FirFile) {
resolveFileAnnotations(
firFile = firDeclaration,
annotations = firDeclaration.annotations,
moduleFileCache = moduleFileCache,
scopeSession = scopeSession,
checkPCE = checkPCE
)
firDeclaration
} else {
val toPhase =
if (toResolveType == ResolveType.AnnotationType) FirResolvePhase.TYPES else FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS
lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
moduleFileCache = moduleFileCache,
toPhase = toPhase,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
}
ResolveType.ClassSuperTypes -> {
require(firDeclaration is FirClassLikeDeclaration) {
"ClassSuperTypes type cannot be applied to ${firDeclaration::class.qualifiedName}"
}
lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.SUPER_TYPES,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
ResolveType.CallableContracts -> {
require(firDeclaration is FirCallableDeclaration) {
"CallableContracts type cannot be applied to ${firDeclaration::class.qualifiedName}"
}
lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.CONTRACTS,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
ResolveType.DeclarationStatus -> {
require(firDeclaration !is FirFile) {
"DeclarationStatus type cannot be applied to ${firDeclaration::class.qualifiedName}"
}
lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.STATUS,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
ResolveType.ValueParametersTypes, ResolveType.TypeParametersTypes -> {
require(firDeclaration !is FirFile) {
"ValueParametersTypes and TypeParametersTypes types cannot be applied to ${firDeclaration::class.qualifiedName}"
}
lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.TYPES,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
}
}
@@ -0,0 +1,405 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.realPsi
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.transformers.FirImportResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.tryCollectDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.runCustomResolveUnderLock
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firIdeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirFileAnnotationsResolveTransformer
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirProviderInterceptorForIDE
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.LazyTransformerFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirElementFinder
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkCanceled
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.findSourceNonLocalFirDeclaration
internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBuilder) {
/**
* Fully resolve file annotations (synchronized)
* @see resolveFileAnnotationsWithoutLock not synchronized
*/
fun resolveFileAnnotations(
firFile: FirFile,
annotations: List<FirAnnotation>,
moduleFileCache: ModuleFileCache,
scopeSession: ScopeSession,
checkPCE: Boolean,
collector: FirTowerDataContextCollector? = null,
) {
if (firFile.resolvePhase >= FirResolvePhase.IMPORTS && annotations.all { it.resolved }) return
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(firFile, checkPCE) {
resolveFileAnnotationsWithoutLock(
firFile = firFile,
annotations = annotations,
scopeSession = scopeSession,
collector = collector
)
}
}
/**
* Fully resolve file annotations (not synchronized)
* @see resolveFileAnnotations synchronized version
*/
private fun resolveFileAnnotationsWithoutLock(
firFile: FirFile,
annotations: List<FirAnnotation>,
scopeSession: ScopeSession,
collector: FirTowerDataContextCollector? = null,
) {
if (firFile.resolvePhase < FirResolvePhase.IMPORTS) {
resolveFileToImportsWithoutLock(firFile, false)
}
if (!annotations.all { it.resolved }) {
FirFileAnnotationsResolveTransformer(
firFile = firFile,
annotations = annotations,
session = firFile.moduleData.session,
scopeSession = scopeSession,
firTowerDataContextCollector = collector,
).transformDeclaration(firFileBuilder.firPhaseRunner)
}
}
private fun FirDeclaration.isValidForResolve(): Boolean = when (origin) {
is FirDeclarationOrigin.Source,
is FirDeclarationOrigin.ImportedFromObject,
is FirDeclarationOrigin.Delegated,
is FirDeclarationOrigin.Synthetic,
is FirDeclarationOrigin.SubstitutionOverride,
is FirDeclarationOrigin.SamConstructor,
is FirDeclarationOrigin.IntersectionOverride -> {
when (this) {
is FirFile -> true
is FirSyntheticProperty -> false
is FirSyntheticPropertyAccessor -> false
is FirSimpleFunction,
is FirProperty,
is FirPropertyAccessor,
is FirField,
is FirTypeAlias,
is FirConstructor -> resolvePhase < FirResolvePhase.BODY_RESOLVE
else -> true
}
}
else -> {
check(resolvePhase == FirResolvePhase.BODY_RESOLVE) {
"Expected body resolve phase for origin $origin but found $resolvePhase"
}
false
}
}
fun lazyResolveFileDeclaration(
firFile: FirFile,
moduleFileCache: ModuleFileCache,
toPhase: FirResolvePhase,
scopeSession: ScopeSession,
checkPCE: Boolean = false,
) {
if (toPhase == FirResolvePhase.RAW_FIR) return
resolveFileToImports(firFile, moduleFileCache, checkPCE)
if (toPhase == FirResolvePhase.IMPORTS) return
if (firFile.resolvePhase >= toPhase) return
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(firFile, checkPCE) {
ResolveTreeBuilder.resolveEnsure(firFile, toPhase) {
lazyResolveFileDeclarationWithoutLock(
firFile = firFile,
moduleFileCache = moduleFileCache,
toPhase = toPhase,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
}
}
private fun resolveFileToImports(firFile: FirFile, moduleFileCache: ModuleFileCache, checkPCE: Boolean) {
if (firFile.resolvePhase >= FirResolvePhase.IMPORTS) return
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(firFile, checkPCE) {
resolveFileToImportsWithoutLock(firFile, checkPCE)
}
}
private fun resolveFileToImportsWithoutLock(firFile: FirFile, checkPCE: Boolean) {
if (firFile.resolvePhase >= FirResolvePhase.IMPORTS) return
if (checkPCE) checkCanceled()
firFile.transform<FirElement, Any?>(FirImportResolveTransformer(firFile.moduleData.session), null)
firFile.replaceResolvePhase(FirResolvePhase.IMPORTS)
}
private fun lazyResolveFileDeclarationWithoutLock(
firFile: FirFile,
moduleFileCache: ModuleFileCache,
toPhase: FirResolvePhase,
scopeSession: ScopeSession,
checkPCE: Boolean = false,
collector: FirTowerDataContextCollector? = null,
) {
if (toPhase == FirResolvePhase.RAW_FIR) return
resolveFileToImportsWithoutLock(firFile, checkPCE)
if (toPhase == FirResolvePhase.IMPORTS) return
if (checkPCE) checkCanceled()
resolveFileAnnotationsWithoutLock(firFile, firFile.annotations, scopeSession, collector)
val validForResolveDeclarations = firFile.declarations
.filter { it.isValidForResolve() && it.resolvePhase < toPhase }
if (validForResolveDeclarations.isEmpty()) return
val designations = validForResolveDeclarations.map {
FirDeclarationDesignationWithFile(path = emptyList(), declaration = it, firFile = firFile)
}
var currentPhase = FirResolvePhase.IMPORTS
while (currentPhase < toPhase) {
currentPhase = currentPhase.next
if (currentPhase.pluginPhase) continue
if (checkPCE) checkCanceled()
val transformersToApply = designations.mapNotNull {
val needToResolve = it.declaration.resolvePhase < currentPhase
if (needToResolve) {
LazyTransformerFactory.createLazyTransformer(
phase = currentPhase,
designation = it,
scopeSession = scopeSession,
moduleFileCache = moduleFileCache,
lazyDeclarationResolver = this,
towerDataContextCollector = null,
firProviderInterceptor = null,
checkPCE = checkPCE,
)
} else null
}
if (transformersToApply.isEmpty()) continue
firFileBuilder.firPhaseRunner.runPhaseWithCustomResolve(currentPhase) {
for (currentTransformer in transformersToApply) {
currentTransformer.transformDeclaration(firFileBuilder.firPhaseRunner)
}
}
firFile.replaceResolvePhase(currentPhase)
}
}
private fun fastTrackForImportsPhase(
firDeclarationToResolve: FirDeclaration,
moduleFileCache: ModuleFileCache,
checkPCE: Boolean,
): Boolean {
val provider = firDeclarationToResolve.moduleData.session.firProvider
val firFile = when (firDeclarationToResolve) {
is FirFile -> firDeclarationToResolve
is FirCallableDeclaration -> provider.getFirCallableContainerFile(firDeclarationToResolve.symbol)
is FirClassLikeDeclaration -> provider.getFirClassifierContainerFile(firDeclarationToResolve.symbol)
else -> null
} ?: return false
resolveFileToImports(firFile, moduleFileCache, checkPCE)
return true
}
/**
* Run designated resolve only designation with fully resolved path (synchronized).
* Suitable for body resolve or/and on-air resolve.
* @see lazyResolveDeclaration for ordinary resolve
* @param firDeclarationToResolve target non-local declaration
*/
fun <D : FirDeclaration> lazyResolveDeclaration(
firDeclarationToResolve: D,
moduleFileCache: ModuleFileCache,
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
checkPCE: Boolean,
skipLocalDeclaration: Boolean = false,
): D {
if (toPhase == FirResolvePhase.RAW_FIR) return firDeclarationToResolve
if (toPhase == FirResolvePhase.IMPORTS) {
if (fastTrackForImportsPhase(firDeclarationToResolve, moduleFileCache, checkPCE)) {
return firDeclarationToResolve
}
}
if (!firDeclarationToResolve.isValidForResolve()) return firDeclarationToResolve
if (firDeclarationToResolve.resolvePhase >= toPhase) return firDeclarationToResolve
if (firDeclarationToResolve is FirFile) {
lazyResolveFileDeclaration(
firFile = firDeclarationToResolve,
moduleFileCache = moduleFileCache,
toPhase = toPhase,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
return firDeclarationToResolve
}
val requestedDeclarationDesignation = firDeclarationToResolve.tryCollectDesignationWithFile()
val designation: FirDeclarationDesignationWithFile
val neededPhase: FirResolvePhase
val isLocalDeclarationResolveRequested: Boolean
if (requestedDeclarationDesignation != null) {
designation = requestedDeclarationDesignation
neededPhase = toPhase
isLocalDeclarationResolveRequested = false
} else {
val possiblyLocalDeclaration = firDeclarationToResolve.getKtDeclarationForFirElement()
val nonLocalDeclaration = possiblyLocalDeclaration.getNonLocalContainingOrThisDeclaration()
?: error("Container for local declaration cannot be null")
isLocalDeclarationResolveRequested = possiblyLocalDeclaration != nonLocalDeclaration
if (isLocalDeclarationResolveRequested && skipLocalDeclaration) return firDeclarationToResolve
val nonLocalFirDeclaration = nonLocalDeclaration.findSourceNonLocalFirDeclaration(
firFileBuilder,
firDeclarationToResolve.moduleData.session.firIdeProvider.symbolProvider,
moduleFileCache
)
neededPhase = if (isLocalDeclarationResolveRequested) FirResolvePhase.BODY_RESOLVE else toPhase
if (nonLocalFirDeclaration.resolvePhase >= neededPhase) return firDeclarationToResolve
if (!nonLocalFirDeclaration.isValidForResolve()) return firDeclarationToResolve
designation = nonLocalFirDeclaration.collectDesignationWithFile()
}
if (designation.declaration.resolvePhase >= neededPhase) return firDeclarationToResolve
if (neededPhase == FirResolvePhase.IMPORTS) {
resolveFileToImports(designation.firFile, moduleFileCache, checkPCE)
return firDeclarationToResolve
}
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(designation.firFile, checkPCE) {
ResolveTreeBuilder.resolveEnsure(designation.declaration, neededPhase) {
runLazyDesignatedResolveWithoutLock(
designation = designation,
moduleFileCache = moduleFileCache,
scopeSession = scopeSession,
toPhase = neededPhase,
checkPCE = checkPCE,
)
designation.declaration
}
}
if (!isLocalDeclarationResolveRequested) return firDeclarationToResolve
return remapDeclarationInContainerIfNeeded(firDeclarationToResolve, designation.declaration, toPhase)
}
private fun <D : FirDeclaration> remapDeclarationInContainerIfNeeded(
declarationToRemap: D,
firContainer: FirDeclaration,
firResolvePhase: FirResolvePhase
): D {
if (declarationToRemap.resolvePhase >= firResolvePhase) return declarationToRemap
val realPsi = declarationToRemap.realPsi
check(realPsi != null) {
"Cannot remap element without PSI"
}
@Suppress("UNCHECKED_CAST")
val firDeclaration = FirElementFinder.findElementIn<FirDeclaration>(firContainer) {
it.realPsi == realPsi
} as? D
check(firDeclaration != null) {
"Containing declaration was resolved but local didn't found in it"
}
check(firDeclaration.resolvePhase >= firResolvePhase) {
"Found local declaration wasn't completely resolved"
}
return firDeclaration
}
private fun runLazyDesignatedResolveWithoutLock(
designation: FirDeclarationDesignationWithFile,
moduleFileCache: ModuleFileCache,
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
checkPCE: Boolean,
) {
if (toPhase == FirResolvePhase.RAW_FIR) return
resolveFileToImportsWithoutLock(designation.firFile, checkPCE)
if (toPhase == FirResolvePhase.IMPORTS) return
val declarationResolvePhase = designation.declaration.resolvePhase
if (declarationResolvePhase >= toPhase) return
var currentPhase = maxOf(declarationResolvePhase, FirResolvePhase.IMPORTS)
while (currentPhase < toPhase) {
currentPhase = currentPhase.next
if (currentPhase.pluginPhase) continue
if (checkPCE) checkCanceled()
LazyTransformerFactory.createLazyTransformer(
phase = currentPhase,
designation = designation,
scopeSession = scopeSession,
moduleFileCache = moduleFileCache,
lazyDeclarationResolver = this,
towerDataContextCollector = null,
firProviderInterceptor = null,
checkPCE = checkPCE,
).transformDeclaration(firFileBuilder.firPhaseRunner)
}
}
internal fun runLazyDesignatedOnAirResolveToBodyWithoutLock(
designation: FirDeclarationDesignationWithFile,
moduleFileCache: ModuleFileCache,
checkPCE: Boolean,
onAirCreatedDeclaration: Boolean,
towerDataContextCollector: FirTowerDataContextCollector?,
) {
resolveFileToImportsWithoutLock(designation.firFile, checkPCE)
var currentPhase = maxOf(designation.declaration.resolvePhase, FirResolvePhase.IMPORTS)
val scopeSession = ScopeSession()
val firProviderInterceptor = if (onAirCreatedDeclaration) {
FirProviderInterceptorForIDE.createForFirElement(
session = designation.firFile.moduleData.session,
firFile = designation.firFile,
element = designation.declaration
)
} else null
while (currentPhase < FirResolvePhase.BODY_RESOLVE) {
currentPhase = currentPhase.next
if (currentPhase.pluginPhase) continue
if (checkPCE) checkCanceled()
LazyTransformerFactory.createLazyTransformer(
phase = currentPhase,
designation = designation,
scopeSession = scopeSession,
moduleFileCache = moduleFileCache,
lazyDeclarationResolver = this,
towerDataContextCollector = towerDataContextCollector,
firProviderInterceptor = firProviderInterceptor,
checkPCE = checkPCE,
).transformDeclaration(firFileBuilder.firPhaseRunner)
}
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.parentOfType
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
internal fun FirDeclaration.getKtDeclarationForFirElement(): KtDeclaration {
require(this !is FirFile)
val ktDeclaration = (psi as? KtDeclaration) ?: run {
(source as? FirFakeSourceElement).psi?.parentOfType()
}
check(ktDeclaration is KtDeclaration) {
"FirDeclaration should have a PSI of type KtDeclaration"
}
val declaration = when (this) {
is FirPropertyAccessor, is FirTypeParameter, is FirValueParameter -> {
when (ktDeclaration) {
is KtPropertyAccessor -> ktDeclaration.property
is KtProperty -> ktDeclaration
is KtParameter, is KtTypeParameter -> {
val containingDeclaration = ktDeclaration.getParentOfType<KtDeclaration>(true)
if (containingDeclaration !is KtPropertyAccessor) containingDeclaration else containingDeclaration.property
}
is KtCallExpression -> {
check(this.source?.kind == FirFakeSourceElementKind.DefaultAccessor)
((ktDeclaration as? KtCallExpression)?.parent as? KtPropertyDelegate)?.parent as? KtProperty
}
else -> ktDeclaration
}
}
else -> ktDeclaration
}
check(declaration is KtDeclaration) {
"FirDeclaration should have a PSI of type KtDeclaration"
}
return declaration
}
internal fun declarationCanBeLazilyResolved(declaration: KtDeclaration): Boolean {
return when (declaration) {
!is KtNamedDeclaration -> false
is KtDestructuringDeclarationEntry, is KtFunctionLiteral, is KtTypeParameter -> false
is KtPrimaryConstructor -> false
is KtParameter -> declaration.hasValOrVar() && declaration.containingClassOrObject?.getClassId() != null
is KtCallableDeclaration, is KtEnumEntry -> {
when (val parent = declaration.parent) {
is KtFile -> true
is KtClassBody -> (parent.parent as? KtClassOrObject)?.getClassId() != null
else -> false
}
}
is KtClassLikeDeclaration -> declaration.getClassId() != null
else -> error("Unexpected ${declaration::class.qualifiedName}")
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtElement
internal fun buildFileFirAnnotation(
session: FirSession,
baseScopeProvider: FirScopeProvider,
fileAnnotation: KtAnnotationEntry,
replacement: RawFirReplacement? = null
): FirAnnotation {
val replacementApplier = replacement?.Applier()
val builder = object : RawFirBuilder(session, baseScopeProvider, psiMode = PsiHandlingMode.IDE) {
inner class VisitorWithReplacement : Visitor() {
override fun convertElement(element: KtElement): FirElement? =
super.convertElement(replacementApplier?.tryReplace(element) ?: element)
}
}
builder.context.packageFqName = fileAnnotation.containingKtFile.packageFqName
val result = builder.VisitorWithReplacement().convertElement(fileAnnotation) as FirAnnotation
replacementApplier?.ensureApplied()
return result
}
@@ -0,0 +1,143 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.builder.BodyBuildingMode
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignation
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
internal class RawFirNonLocalDeclarationBuilder private constructor(
session: FirSession,
baseScopeProvider: FirScopeProvider,
private val declarationToBuild: KtDeclaration,
private val functionsToRebind: Set<FirFunction>? = null,
private val replacementApplier: RawFirReplacement.Applier? = null
) : RawFirBuilder(session, baseScopeProvider, psiMode = PsiHandlingMode.IDE, bodyBuildingMode = BodyBuildingMode.NORMAL) {
companion object {
fun buildWithReplacement(
session: FirSession,
scopeProvider: FirScopeProvider,
designation: FirDeclarationDesignation,
rootNonLocalDeclaration: KtDeclaration,
replacement: RawFirReplacement?
): FirDeclaration {
val replacementApplier = replacement?.Applier()
val builder = RawFirNonLocalDeclarationBuilder(
session = session,
baseScopeProvider = scopeProvider,
declarationToBuild = rootNonLocalDeclaration,
replacementApplier = replacementApplier
)
builder.context.packageFqName = rootNonLocalDeclaration.containingKtFile.packageFqName
return builder.moveNext(designation.path.iterator(), containingClass = null).also {
replacementApplier?.ensureApplied()
}
}
fun buildWithFunctionSymbolRebind(
session: FirSession,
scopeProvider: FirScopeProvider,
designation: FirDeclarationDesignation,
rootNonLocalDeclaration: KtDeclaration,
): FirDeclaration {
val functionsToRebind = when (val originalDeclaration = designation.declaration) {
is FirSimpleFunction -> setOf(originalDeclaration)
is FirProperty -> setOfNotNull(originalDeclaration.getter, originalDeclaration.setter)
else -> null
}
val builder = RawFirNonLocalDeclarationBuilder(
session = session,
baseScopeProvider = scopeProvider,
declarationToBuild = rootNonLocalDeclaration,
functionsToRebind = functionsToRebind,
)
builder.context.packageFqName = rootNonLocalDeclaration.containingKtFile.packageFqName
return builder.moveNext(designation.path.iterator(), containingClass = null)
}
}
override fun bindFunctionTarget(target: FirFunctionTarget, function: FirFunction) {
val rewrittenTarget = functionsToRebind?.firstOrNull { it.realPsi == function.realPsi } ?: function
super.bindFunctionTarget(target, rewrittenTarget)
}
private inner class VisitorWithReplacement : Visitor() {
override fun convertElement(element: KtElement): FirElement? =
super.convertElement(replacementApplier?.tryReplace(element) ?: element)
override fun convertProperty(
property: KtProperty,
ownerRegularOrAnonymousObjectSymbol: FirClassSymbol<*>?,
ownerRegularClassTypeParametersCount: Int?
): FirProperty {
val replacementProperty = replacementApplier?.tryReplace(property) ?: property
check(replacementProperty is KtProperty)
return super.convertProperty(
property = replacementProperty,
ownerRegularOrAnonymousObjectSymbol = ownerRegularOrAnonymousObjectSymbol,
ownerRegularClassTypeParametersCount = ownerRegularClassTypeParametersCount
)
}
override fun convertValueParameter(
valueParameter: KtParameter,
defaultTypeRef: FirTypeRef?,
valueParameterDeclaration: ValueParameterDeclaration
): FirValueParameter {
val replacementParameter = replacementApplier?.tryReplace(valueParameter) ?: valueParameter
check(replacementParameter is KtParameter)
return super.convertValueParameter(
valueParameter = replacementParameter,
defaultTypeRef = defaultTypeRef,
valueParameterDeclaration = valueParameterDeclaration
)
}
}
private fun moveNext(iterator: Iterator<FirDeclaration>, containingClass: FirRegularClass?): FirDeclaration {
if (!iterator.hasNext()) {
val visitor = VisitorWithReplacement()
return when (declarationToBuild) {
is KtProperty -> {
val ownerSymbol = containingClass?.symbol
val ownerTypeArgumentsCount = containingClass?.typeParameters?.size
visitor.convertProperty(declarationToBuild, ownerSymbol, ownerTypeArgumentsCount)
}
else -> visitor.convertElement(declarationToBuild)
} as FirDeclaration
}
val parent = iterator.next()
if (parent !is FirRegularClass) return moveNext(iterator, containingClass = null)
val classOrObject = parent.psi
check(classOrObject is KtClassOrObject)
withChildClassName(classOrObject.nameAsSafeName, isExpect = classOrObject.hasExpectModifier() || context.containerIsExpect) {
withCapturedTypeParameters(parent.isInner, parent.typeParameters.subList(0, classOrObject.typeParameters.size)) {
registerSelfType(classOrObject.toDelegatedSelfType(parent))
return moveNext(iterator, parent)
}
}
}
private fun PsiElement?.toDelegatedSelfType(firClass: FirRegularClass): FirResolvedTypeRef =
toDelegatedSelfType(firClass.typeParameters, firClass.symbol)
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.psi.*
internal data class RawFirReplacement(val from: KtElement, val to: KtElement) {
companion object {
fun isApplicableForReplacement(element: KtElement) = when (element) {
is KtFile, is KtClassInitializer, is KtClassOrObject, is KtObjectLiteralExpression, is KtTypeAlias,
is KtNamedFunction, is KtLambdaExpression, is KtAnonymousInitializer, is KtProperty, is KtTypeReference,
is KtAnnotationEntry, is KtTypeParameter, is KtTypeProjection, is KtParameter, is KtBlockExpression,
is KtSimpleNameExpression, is KtConstantExpression, is KtStringTemplateExpression, is KtReturnExpression,
is KtTryExpression, is KtIfExpression, is KtWhenExpression, is KtDoWhileExpression, is KtWhileExpression,
is KtForExpression, is KtBreakExpression, is KtContinueExpression, is KtBinaryExpression, is KtBinaryExpressionWithTypeRHS,
is KtIsExpression, is KtUnaryExpression, is KtCallExpression, is KtArrayAccessExpression, is KtQualifiedExpression,
is KtThisExpression, is KtSuperExpression, is KtParenthesizedExpression, is KtLabeledExpression, is KtAnnotatedExpression,
is KtThrowExpression, is KtDestructuringDeclaration, is KtClassLiteralExpression, is KtCallableReferenceExpression,
is KtCollectionLiteralExpression -> true
else -> false
}
}
inner class Applier {
private var replacementApplied = false
private fun ensureReplacementIsValid() {
require(from == to || isApplicableForReplacement(from)) {
"Replacement is possible for applicable type but given ${from::class.simpleName}"
}
require(from::class == to::class) {
"Replacement is possible for same type in replacements but given\n${from::class.simpleName} and ${to::class.simpleName}"
}
}
fun tryReplace(element: KtElement): KtElement {
if (from != element) return element
ensureReplacementIsValid()
replacementApplied = true
return to
}
fun ensureApplied() {
check(from == to || replacementApplied) {
"Replacement requested but was not applied for ${from::class.simpleName}"
}
}
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
import org.jetbrains.kotlin.psi.KtTypeReference
internal fun buildFirUserTypeRef(
typeReference: KtTypeReference,
session: FirSession,
baseScopeProvider: FirScopeProvider
): FirUserTypeRef {
val builder = object : RawFirBuilder(session, baseScopeProvider, psiMode = PsiHandlingMode.IDE) {
fun build(): FirUserTypeRef = Visitor().visitTypeReference(typeReference, Unit) as FirUserTypeRef
}
builder.context.packageFqName = typeReference.containingKtFile.packageFqName
return builder.build()
}
@@ -0,0 +1,154 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFileSymbol
import java.io.File
internal object ResolveTreeBuilder {
private val file: File? by lazy(LazyThreadSafetyMode.PUBLICATION) {
System.getProperty("fir.lazy.resolve.dump")?.let {
File(it)
}
}
class Context(var rootCall: Boolean = true, var underLock: Boolean = false, val builder: StringBuilder = StringBuilder())
val currentContext: ThreadLocal<Context> = ThreadLocal.withInitial { Context() }
private fun FirResolvePhase.firPhaseName(): String = when (this) {
FirResolvePhase.RAW_FIR -> "Raw"
FirResolvePhase.IMPORTS -> "Imports"
FirResolvePhase.SUPER_TYPES -> "SuperTypes"
FirResolvePhase.TYPES -> "Types"
FirResolvePhase.STATUS -> "Status"
FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS -> "ArgumentsOfAnnotations"
FirResolvePhase.CONTRACTS -> "Contracts"
FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE -> "ImplicitTypes"
FirResolvePhase.BODY_RESOLVE -> "Body"
else -> "?"
}
fun resolvePhase(
declaration: FirDeclaration,
phase: FirResolvePhase,
body: () -> Unit
) {
newNodeContext {
resolveToDeclaration(declaration, "Phase", false, phase) {
body()
declaration
}
}
}
fun resolveEnsure(
declaration: FirDeclaration,
phase: FirResolvePhase,
body: () -> Unit
) {
newNodeContext {
resolveToDeclaration(declaration, "Ensure", true, phase) {
body()
declaration
}
}
}
inline fun <T> lockNode(startTime: Long, body: () -> T): T {
if (file == null) return body()
val waitEnd = System.currentTimeMillis()
val currentContext = currentContext.get()
val builderLength = currentContext.builder.length
val wasUnderLock = currentContext.underLock
currentContext.underLock = true
return newNodeContext {
try {
body()
} finally {
val contentionTime = waitEnd - startTime
val executionTime = System.currentTimeMillis() - waitEnd
if (!wasUnderLock && builderLength != currentContext.builder.length) {
val tag = "<UnderLock waitTime=\"$contentionTime\" executionTime=\"$executionTime\">"
currentContext.builder.insert(builderLength, tag)
currentContext.builder.append("</UnderLock>")
}
currentContext.underLock = wasUnderLock
}
}
}
fun <T : FirDeclaration> resolveReturnTypeCalculation(
declaration: FirDeclaration,
body: () -> T
): T = newNodeContext {
resolveToDeclaration(declaration, "RTC", true, null, body)
}
private fun FirDeclaration.toDeclarationName(): String = when (val symbol = symbol) {
is FirClassSymbol<*> -> symbol.classId.asSingleFqName().asString()
is FirCallableSymbol<*> -> symbol.callableId.asSingleFqName().asString()
is FirFileSymbol -> symbol.fir.name
else -> symbol::class.qualifiedName
} ?: "${this::class.qualifiedName}:${hashCode()}"
private fun <T : FirDeclaration> resolveToDeclaration(
declaration: FirDeclaration,
nodeName: String,
needWriteDeclarationName: Boolean,
phase: FirResolvePhase? = null,
body: () -> T
): T {
if (phase != null && declaration.resolvePhase >= phase) return body()
val currentContext = currentContext.get()
val currentPosition = currentContext.builder.length
var withException = true
val start = System.currentTimeMillis()
try {
return body().also {
withException = false
}
} finally {
val end = System.currentTimeMillis()
val timeAttr = " time=\"${end - start}\""
val phaseAttr = if (phase != null) " phase=\"${phase.firPhaseName()}\"" else ""
val declarationAttr = if (needWriteDeclarationName) " declaration=\"${declaration.toDeclarationName()}\"" else ""
val exceptionAttr = if (withException) " withException=\"true\"" else ""
val nodeContent = "$declarationAttr$phaseAttr$timeAttr$exceptionAttr"
if (currentPosition == currentContext.builder.length) {
currentContext.builder.append("<$nodeName$nodeContent />")
} else {
currentContext.builder.insert(currentPosition, "<$nodeName$nodeContent>")
currentContext.builder.append("</$nodeName>")
}
}
}
private inline fun <T> newNodeContext(body: () -> T): T {
val dumpFile = file ?: return body()
val currentContext = currentContext.get()
val oldRootEnsure = currentContext.rootCall
try {
currentContext.rootCall = false
return body()
} finally {
currentContext.rootCall = oldRootEnsure
if (oldRootEnsure) {
synchronized(dumpFile) {
dumpFile.appendText(currentContext.builder.toString())
}
currentContext.builder.clear()
}
}
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.caches.createCache
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirBuiltinSymbolProvider
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
class FirIdeBuiltinSymbolProvider(
session: FirSession,
moduleData: FirModuleData,
kotlinScopeProvider: FirKotlinScopeProvider
) : FirBuiltinSymbolProvider(session, moduleData, kotlinScopeProvider) {
@OptIn(ExperimentalStdlibApi::class)
private val functionsCache = session.firCachesFactory.createCache { callableId: CallableId ->
buildList {
getTopLevelFunctionSymbolsToByPackageFragments(this, callableId.packageName, callableId.callableName)
}
}
@OptIn(FirSymbolProviderInternals::class)
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
// valid until FirBuiltinSymbolProvider provide property symbols
destination += functionsCache.getValue(CallableId(packageFqName, name))
}
@OptIn(FirSymbolProviderInternals::class)
override fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
destination += functionsCache.getValue(CallableId(packageFqName, name))
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
internal class FirIdeBuiltinsAndCloneableSessionProvider(override val symbolProvider: FirSymbolProvider) : FirProvider() {
override fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration? =
symbolProvider.getClassLikeSymbolByClassId(classId)?.fir
override fun getFirClassifierContainerFile(fqName: ClassId): FirFile = shouldNotBeCalled()
override fun getFirClassifierContainerFileIfAny(fqName: ClassId): FirFile? = null
override fun getFirCallableContainerFile(symbol: FirCallableSymbol<*>): FirFile? = null
override fun getFirFilesByPackage(fqName: FqName): List<FirFile> = emptyList()
@FirProviderInternals
override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) = shouldNotBeCalled()
@FirProviderInternals
override fun recordGeneratedMember(owner: FirAnnotatedDeclaration, klass: FirDeclaration) = shouldNotBeCalled()
override fun getClassNamesInPackage(fqName: FqName): Set<Name> = shouldNotBeCalled()
private fun shouldNotBeCalled(): Nothing = error("Should not be called for FirIdeBuiltinsAndCloneableSession")
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
internal class FirIdeLibrariesSessionProvider(
override val symbolProvider: FirSymbolProvider
) : FirProvider() {
override fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration? =
symbolProvider.getClassLikeSymbolByClassId(classId)?.fir
override fun getFirClassifierContainerFile(fqName: ClassId): FirFile = shouldNotBeCalled()
override fun getFirClassifierContainerFileIfAny(fqName: ClassId): FirFile? = null
override fun getFirCallableContainerFile(symbol: FirCallableSymbol<*>): FirFile? = null
override fun getFirFilesByPackage(fqName: FqName): List<FirFile> = emptyList()
@FirProviderInternals
override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) = shouldNotBeCalled()
@FirProviderInternals
override fun recordGeneratedMember(owner: FirAnnotatedDeclaration, klass: FirDeclaration) = shouldNotBeCalled()
override fun getClassNamesInPackage(fqName: FqName): Set<Name> = shouldNotBeCalled()
private fun shouldNotBeCalled(): Nothing = error("Should not be called for FirIdeLibrariesSessionProvider")
}
@@ -0,0 +1,138 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.KotlinPackageProvider
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.ThreadSafeMutableState
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@ThreadSafeMutableState
internal class FirIdeProvider(
@Suppress("UNUSED_PARAMETER") project: Project,
val session: FirSession,
@Suppress("UNUSED_PARAMETER") moduleInfo: ModuleSourceInfoBase,
val kotlinScopeProvider: FirKotlinScopeProvider,
firFileBuilder: FirFileBuilder,
val cache: ModuleFileCache,
private val declarationProvider: KotlinDeclarationProvider,
packageProvider: KotlinPackageProvider,
) : FirProvider() {
override val symbolProvider: FirSymbolProvider = SymbolProvider()
private val providerHelper = FirProviderHelper(
cache,
firFileBuilder,
declarationProvider,
packageProvider,
)
override val isPhasedFirAllowed: Boolean get() = true
override fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration? =
providerHelper.getFirClassifierByFqName(classId)
override fun getFirClassifierContainerFile(fqName: ClassId): FirFile {
return getFirClassifierContainerFileIfAny(fqName)
?: error("Couldn't find container for $fqName")
}
override fun getFirClassifierContainerFileIfAny(fqName: ClassId): FirFile? {
val fir = getFirClassifierByFqName(fqName) ?: return null // Necessary to ensure cacheProvider contains this classifier
return cache.getContainerFirFile(fir)
}
override fun getFirClassifierContainerFile(symbol: FirClassLikeSymbol<*>): FirFile {
return getFirClassifierContainerFileIfAny(symbol)
?: error("Couldn't find container for ${symbol.classId}")
}
override fun getFirClassifierContainerFileIfAny(symbol: FirClassLikeSymbol<*>): FirFile? =
cache.getContainerFirFile(symbol.fir)
override fun getFirCallableContainerFile(symbol: FirCallableSymbol<*>): FirFile? {
symbol.fir.originalForSubstitutionOverride?.symbol?.let {
return getFirCallableContainerFile(it)
}
if (symbol is FirAccessorSymbol) {
val fir = symbol.fir
if (fir is FirSyntheticProperty) {
return getFirCallableContainerFile(fir.getter.delegate.symbol)
}
}
return cache.getContainerFirFile(symbol.fir)
}
override fun getFirFilesByPackage(fqName: FqName): List<FirFile> = error("Should not be called in FIR IDE")
@FirProviderInternals
override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) {
TODO()
}
@FirProviderInternals
override fun recordGeneratedMember(owner: FirAnnotatedDeclaration, klass: FirDeclaration) {
TODO()
}
override fun getClassNamesInPackage(fqName: FqName): Set<Name> =
declarationProvider.getClassNamesInPackage(fqName)
@NoMutableState
private inner class SymbolProvider : FirSymbolProvider(session) {
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> =
providerHelper.getTopLevelCallableSymbols(packageFqName, name)
@FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
destination += getTopLevelCallableSymbols(packageFqName, name)
}
override fun getTopLevelFunctionSymbols(packageFqName: FqName, name: Name): List<FirNamedFunctionSymbol> =
providerHelper.getTopLevelFunctionSymbols(packageFqName, name)
@FirSymbolProviderInternals
override fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
destination += getTopLevelFunctionSymbols(packageFqName, name)
}
override fun getTopLevelPropertySymbols(packageFqName: FqName, name: Name): List<FirPropertySymbol> =
providerHelper.getTopLevelPropertySymbols(packageFqName, name)
@FirSymbolProviderInternals
override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) {
destination += getTopLevelPropertySymbols(packageFqName, name)
}
override fun getPackage(fqName: FqName): FqName? =
providerHelper.getPackage(fqName)
override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? {
return getFirClassifierByFqName(classId)?.symbol
}
}
}
internal val FirSession.firIdeProvider: FirIdeProvider by FirSession.sessionComponentAccessor()
@@ -0,0 +1,130 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.providers.FirDependenciesSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
internal class FirModuleWithDependenciesSymbolProvider(
session: FirSession,
private val providers: List<FirSymbolProvider>,
val dependencyProvider: DependentModuleProviders
) : FirSymbolProvider(session) {
override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? =
getClassLikeSymbolByFqNameWithoutDependencies(classId)
?: dependencyProvider.getClassLikeSymbolByClassId(classId)
fun getClassLikeSymbolByFqNameWithoutDependencies(classId: ClassId): FirClassLikeSymbol<*>? =
providers.firstNotNullOfOrNull { it.getClassLikeSymbolByClassId(classId) }
@FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
getTopLevelCallableSymbolsToWithoutDependencies(destination, packageFqName, name)
dependencyProvider.getTopLevelCallableSymbolsTo(destination, packageFqName, name)
}
@FirSymbolProviderInternals
fun getTopLevelCallableSymbolsToWithoutDependencies(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
providers.forEach { it.getTopLevelCallableSymbolsTo(destination, packageFqName, name) }
}
@FirSymbolProviderInternals
override fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
getTopLevelFunctionSymbolsToWithoutDependencies(destination, packageFqName, name)
dependencyProvider.getTopLevelFunctionSymbolsTo(destination, packageFqName, name)
}
@FirSymbolProviderInternals
override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) {
getTopLevelPropertySymbolsToWithoutDependencies(destination, packageFqName, name)
dependencyProvider.getTopLevelPropertySymbolsTo(destination, packageFqName, name)
}
@FirSymbolProviderInternals
fun getTopLevelFunctionSymbolsToWithoutDependencies(
destination: MutableList<FirNamedFunctionSymbol>,
packageFqName: FqName,
name: Name
) {
providers.forEach { it.getTopLevelFunctionSymbolsTo(destination, packageFqName, name) }
}
@FirSymbolProviderInternals
fun getTopLevelPropertySymbolsToWithoutDependencies(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) {
providers.forEach { it.getTopLevelPropertySymbolsTo(destination, packageFqName, name) }
}
override fun getPackage(fqName: FqName): FqName? =
getPackageWithoutDependencies(fqName)
?: dependencyProvider.getPackage(fqName)
fun getPackageWithoutDependencies(fqName: FqName): FqName? =
providers.firstNotNullOfOrNull { it.getPackage(fqName) }
}
internal class DependentModuleProviders(session: FirSession, private val providers: List<FirSymbolProvider>) : FirDependenciesSymbolProvider(session) {
override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? =
providers.firstNotNullOfOrNull { provider ->
when (provider) {
is FirModuleWithDependenciesSymbolProvider -> provider.getClassLikeSymbolByFqNameWithoutDependencies(classId)
else -> provider.getClassLikeSymbolByClassId(classId)
}
}
@FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
providers.forEach { provider ->
when (provider) {
is FirModuleWithDependenciesSymbolProvider ->
provider.getTopLevelCallableSymbolsToWithoutDependencies(destination, packageFqName, name)
else -> provider.getTopLevelCallableSymbolsTo(destination, packageFqName, name)
}
}
}
@FirSymbolProviderInternals
override fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
providers.forEach { provider ->
when (provider) {
is FirModuleWithDependenciesSymbolProvider ->
provider.getTopLevelFunctionSymbolsToWithoutDependencies(destination, packageFqName, name)
else -> provider.getTopLevelFunctionSymbolsTo(destination, packageFqName, name)
}
}
}
@FirSymbolProviderInternals
override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) {
providers.forEach { provider ->
when (provider) {
is FirModuleWithDependenciesSymbolProvider ->
provider.getTopLevelPropertySymbolsToWithoutDependencies(destination, packageFqName, name)
else -> provider.getTopLevelPropertySymbolsTo(destination, packageFqName, name)
}
}
}
override fun getPackage(fqName: FqName): FqName? =
providers.firstNotNullOfOrNull { provider ->
when (provider) {
is FirModuleWithDependenciesSymbolProvider -> provider.getPackageWithoutDependencies(fqName)
else -> provider.getPackage(fqName)
}
}
}
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import com.google.common.collect.Sets
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.KotlinPackageProvider
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirElementFinder
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.executeOrReturnDefaultValueOnPCE
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import java.util.*
internal class FirProviderHelper(
private val cache: ModuleFileCache,
private val firFileBuilder: FirFileBuilder,
private val declarationProvider: KotlinDeclarationProvider,
private val packageProvider: KotlinPackageProvider,
) {
fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration? {
if (classId.isLocal) return null
return executeOrReturnDefaultValueOnPCE(null) {
cache.classifierByClassId.computeIfAbsent(classId) {
val ktClass = when (val klass = declarationProvider.getClassesByClassId(classId).firstOrNull()) {
null -> declarationProvider.getTypeAliasesByClassId(classId).firstOrNull()
else -> if (klass.getClassId() == null) null else klass
} ?: return@computeIfAbsent Optional.empty()
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktClass.containingKtFile, cache, preferLazyBodies = true)
val classifier = FirElementFinder.findElementIn<FirClassLikeDeclaration>(firFile) { classifier ->
classifier.symbol.classId == classId
}
?: error("Classifier $classId was found in file ${ktClass.containingKtFile.virtualFilePath} but was not found in FirFile")
Optional.of(classifier)
}.getOrNull()
}
}
fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
val callableId = CallableId(packageFqName, name)
return executeOrReturnDefaultValueOnPCE(emptyList()) {
cache.callableByCallableId.computeIfAbsent(callableId) {
val files = Sets.newIdentityHashSet<KtFile>().apply {
declarationProvider.getTopLevelFunctions(callableId).mapTo(this) { it.containingKtFile }
declarationProvider.getTopLevelProperties(callableId).mapTo(this) { it.containingKtFile }
}
@OptIn(ExperimentalStdlibApi::class)
buildList {
files.forEach { ktFile ->
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, cache, preferLazyBodies = true)
firFile.collectCallableDeclarationsTo(this, name)
}
}
}
}
}
fun getTopLevelFunctionSymbols(packageFqName: FqName, name: Name): List<FirNamedFunctionSymbol> {
return getTopLevelCallableSymbols(packageFqName, name).filterIsInstance<FirNamedFunctionSymbol>()
}
fun getTopLevelPropertySymbols(packageFqName: FqName, name: Name): List<FirPropertySymbol> {
return getTopLevelCallableSymbols(packageFqName, name).filterIsInstance<FirPropertySymbol>()
}
private fun FirFile.collectCallableDeclarationsTo(list: MutableList<FirCallableSymbol<*>>, name: Name) {
declarations.mapNotNullTo(list) { declaration ->
if (declaration is FirCallableDeclaration && declaration.symbol.callableId.callableName == name) {
declaration.symbol
} else null
}
}
fun getPackage(fqName: FqName): FqName? =
fqName.takeIf(packageProvider::isPackageExists)
}
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import java.util.*
internal fun <T: Any> Optional<T>.getOrNull(): T? = orElse(null)
@@ -0,0 +1,131 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.resolver
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.FirArgumentList
import org.jetbrains.kotlin.fir.expressions.FirEmptyArgumentList
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.builder.buildFunctionCall
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
class SingleCandidateResolver(
private val firSession: FirSession,
private val firFile: FirFile,
) {
private val scopeSession = ScopeSession()
// TODO This transformer is not intended for actual transformations and created here only to simplify access to body resolve components
private val stubBodyResolveTransformer = object : FirBodyResolveTransformer(
session = firSession,
phase = FirResolvePhase.BODY_RESOLVE,
implicitTypeOnly = false,
scopeSession = scopeSession,
) {}
private val bodyResolveComponents =
FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents(
firSession,
scopeSession,
stubBodyResolveTransformer,
stubBodyResolveTransformer.context,
)
private val firCallCompleter = FirCallCompleter(
stubBodyResolveTransformer,
bodyResolveComponents,
)
private val resolutionStageRunner = ResolutionStageRunner()
fun resolveSingleCandidate(
resolutionParameters: ResolutionParameters
): FirFunctionCall? {
val infoProvider = createCandidateInfoProvider(resolutionParameters)
if (infoProvider.shouldFailBeforeResolve())
return null
val callInfo = infoProvider.callInfo()
val explicitReceiverKind = infoProvider.explicitReceiverKind()
val dispatchReceiverValue = infoProvider.dispatchReceiverValue()
val implicitExtensionReceiverValue = infoProvider.implicitExtensionReceiverValue()
val resolutionContext = stubBodyResolveTransformer.resolutionContext
val candidate = CandidateFactory(resolutionContext, callInfo).createCandidate(
callInfo,
resolutionParameters.callableSymbol,
explicitReceiverKind = explicitReceiverKind,
dispatchReceiverValue = dispatchReceiverValue,
extensionReceiverValue =
if (explicitReceiverKind.isExtensionReceiver)
callInfo.explicitReceiver?.let { ExpressionReceiverValue(it) }
else
implicitExtensionReceiverValue,
scope = null,
)
val applicability = resolutionStageRunner.processCandidate(candidate, resolutionContext, stopOnFirstError = true)
if (applicability.isSuccess) {
return completeResolvedCandidate(candidate, resolutionParameters)
}
return null
}
private fun createCandidateInfoProvider(resolutionParameters: ResolutionParameters): CandidateInfoProvider {
return when (resolutionParameters.singleCandidateResolutionMode) {
SingleCandidateResolutionMode.CHECK_EXTENSION_FOR_COMPLETION -> CheckExtensionForCompletionCandidateInfoProvider(
resolutionParameters,
firFile,
firSession
)
}
}
private fun completeResolvedCandidate(candidate: Candidate, resolutionParameters: ResolutionParameters): FirFunctionCall? {
val fakeCall = buildFunctionCall {
calleeReference = FirNamedReferenceWithCandidate(
source = null,
name = resolutionParameters.callableSymbol.callableId.callableName,
candidate = candidate
)
}
val expectedType = resolutionParameters.expectedType ?: bodyResolveComponents.noExpectedType
val completionResult = firCallCompleter.completeCall(fakeCall, expectedType)
return if (completionResult.callCompleted) {
completionResult.result
} else null
}
}
class ResolutionParameters(
val singleCandidateResolutionMode: SingleCandidateResolutionMode,
val callableSymbol: FirCallableSymbol<*>,
val implicitReceiver: ImplicitReceiverValue<*>? = null,
val expectedType: FirTypeRef? = null,
val explicitReceiver: FirExpression? = null,
val argumentList: FirArgumentList = FirEmptyArgumentList,
val typeArgumentList: List<FirTypeProjection> = emptyList(),
)
enum class SingleCandidateResolutionMode {
/**
* Run resolution stages necessary to type check extension receiver (explicit/implicit) for candidate function.
* Candidate is expected to be taken from context scope.
* Arguments and type arguments are not expected and not checked.
* Explicit receiver can be passed and will always be interpreted as extension receiver.
*/
CHECK_EXTENSION_FOR_COMPLETION
}
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.resolver
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.inference.receiverType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
/**
* A supplier of information for resolving a call against a single provided candidate.
* Implementors of this interface form a candidate from provided resolution parameters to fit requested resolution mode.
* This includes creating artificial CallInfo, combining receivers and generating CallKind with specific resolution sequence.
*/
interface CandidateInfoProvider {
fun callInfo(): CallInfo
fun callKind(): CallKind
fun explicitReceiverKind(): ExplicitReceiverKind
fun dispatchReceiverValue(): ReceiverValue?
fun implicitExtensionReceiverValue(): ImplicitReceiverValue<*>?
fun shouldFailBeforeResolve(): Boolean
}
abstract class AbstractCandidateInfoProvider(
protected val resolutionParameters: ResolutionParameters,
protected val firFile: FirFile,
protected val firSession: FirSession,
) : CandidateInfoProvider {
override fun callInfo(): CallInfo = with(resolutionParameters) {
CallInfo(
firFile, // TODO: consider passing more precise info here, if needed
callKind = callKind(),
name = callableSymbol.callableId.callableName,
explicitReceiver = explicitReceiver,
argumentList = argumentList,
typeArguments = typeArgumentList,
containingDeclarations = emptyList(), // TODO - maybe we should pass declarations from context here (no visible differences atm)
containingFile = firFile,
isImplicitInvoke = false,
session = firSession,
)
}
override fun shouldFailBeforeResolve(): Boolean = false
}
/**
* Provider for CHECK_EXTENSION_FOR_COMPLETION mode.
*/
class CheckExtensionForCompletionCandidateInfoProvider(
resolutionParameters: ResolutionParameters,
firFile: FirFile,
firSession: FirSession,
) : AbstractCandidateInfoProvider(resolutionParameters, firFile, firSession) {
override fun callKind(): CallKind = buildCallKindWithCustomResolutionSequence {
checkExtensionReceiver = true
}
override fun explicitReceiverKind(): ExplicitReceiverKind =
if (resolutionParameters.explicitReceiver == null)
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER
else ExplicitReceiverKind.EXTENSION_RECEIVER
// Right now it's impossible to reason about dispatch receiver when candidate comes from arbitrary scope with no other information.
// So dispatch receiver is not passed from provider and later not checked during the resolution sequence.
override fun dispatchReceiverValue(): ReceiverValue? = null
override fun implicitExtensionReceiverValue(): ImplicitReceiverValue<*>? = with(resolutionParameters) {
if (explicitReceiver == null) implicitReceiver else null
}
// Candidates with inconsistent extension receivers are skipped in tower resolver before resolution stages.
// Passing them through can lead to false positives.
override fun shouldFailBeforeResolve(): Boolean = with(resolutionParameters) {
val callHasExtensionReceiver = explicitReceiverKind() == ExplicitReceiverKind.EXTENSION_RECEIVER
|| implicitExtensionReceiverValue() != null
val fir = callableSymbol.fir
val candidateHasExtensionReceiver = fir.receiverTypeRef != null
|| fir is FirVariable && fir.returnTypeRef.coneType.receiverType(firSession) != null
callHasExtensionReceiver != candidateHasExtensionReceiver
}
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.fir.BuiltinTypes
import org.jetbrains.kotlin.fir.PrivateSessionConstructor
@OptIn(PrivateSessionConstructor::class)
class FirIdeBuiltinsAndCloneableSession @PrivateSessionConstructor constructor(
override val project: Project,
builtinTypes: BuiltinTypes,
) : FirIdeSession(builtinTypes, Kind.Library)
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.fir.BuiltinTypes
import org.jetbrains.kotlin.fir.PrivateSessionConstructor
/**
* [org.jetbrains.kotlin.fir.FirSession] responsible for all libraries analysing module transitively depends on
*/
@OptIn(PrivateSessionConstructor::class)
internal class FirIdeLibrariesSession @PrivateSessionConstructor constructor(
override val project: Project,
override val scope: GlobalSearchScope,
builtinTypes: BuiltinTypes,
) : FirIdeModuleSession(builtinTypes, Kind.Library)
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.fir.BuiltinTypes
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.PrivateSessionConstructor
@OptIn(PrivateSessionConstructor::class)
abstract class FirIdeSession(override val builtinTypes: BuiltinTypes, kind: Kind) : FirSession(sessionProvider = null, kind) {
abstract val project: Project
}
@OptIn(PrivateSessionConstructor::class)
abstract class FirIdeModuleSession(builtinTypes: BuiltinTypes, kind: Kind) : FirIdeSession(builtinTypes, kind) {
abstract val scope: GlobalSearchScope
}
@@ -0,0 +1,263 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.createPackageProvider
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmTypeMapper
import org.jetbrains.kotlin.fir.caches.FirCachesFactory
import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider
import org.jetbrains.kotlin.fir.java.JavaSymbolProvider
import org.jetbrains.kotlin.fir.java.deserialization.KotlinDeserializedJvmSymbolsProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirDependenciesSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCloneableSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCompositeSymbolProvider
import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.resolve.transformers.FirPhaseCheckingPhaseManager
import org.jetbrains.kotlin.fir.symbols.FirPhaseManager
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.session.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.IdeFirPhaseManager
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveStateConfigurator
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.stateConfigurator
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCacheImpl
import org.jetbrains.kotlin.analysis.low.level.api.fir.fir.caches.FirThreadSafeCachesFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.FirIdeBuiltinsAndCloneableSessionProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.FirIdeLibrariesSessionProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.FirIdeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.FirModuleWithDependenciesSymbolProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkCanceled
import org.jetbrains.kotlin.load.java.createJavaClassFinder
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
@OptIn(PrivateSessionConstructor::class, SessionConfiguration::class)
internal object FirIdeSessionFactory {
fun createSourcesSession(
project: Project,
configurator: FirModuleResolveStateConfigurator,
moduleInfo: ModuleSourceInfoBase,
builtinsAndCloneableSession: FirIdeBuiltinsAndCloneableSession,
firPhaseRunner: FirPhaseRunner,
sessionInvalidator: FirSessionInvalidator,
builtinTypes: BuiltinTypes,
sessionsCache: MutableMap<ModuleSourceInfoBase, FirIdeSourcesSession>,
isRootModule: Boolean,
librariesCache: LibrariesCache,
configureSession: (FirIdeSession.() -> Unit)? = null
): FirIdeSourcesSession {
sessionsCache[moduleInfo]?.let { return it }
val languageVersionSettings = project.stateConfigurator.getLanguageVersionSettings(moduleInfo)
val scopeProvider = FirKotlinScopeProvider(::wrapScopeWithJvmMapped)
val firBuilder = FirFileBuilder(scopeProvider, firPhaseRunner)
val searchScope = project.stateConfigurator.getModuleSourceScope(moduleInfo)
val dependentModules = moduleInfo.dependenciesWithoutSelf()
.filterIsInstanceTo<ModuleSourceInfoBase, MutableList<ModuleSourceInfoBase>>(mutableListOf())
val session = FirIdeSourcesSession(dependentModules, project, searchScope, firBuilder, builtinTypes)
sessionsCache[moduleInfo] = session
return session.apply session@{
val moduleData = FirModuleInfoBasedModuleData(moduleInfo).apply { bindSession(this@session) }
registerModuleData(moduleData)
val cache = ModuleFileCacheImpl(this)
val firPhaseManager = IdeFirPhaseManager(FirLazyDeclarationResolver(firFileBuilder), cache, sessionInvalidator)
registerIdeComponents(project)
registerCommonComponents(languageVersionSettings)
registerCommonJavaComponents(JavaModuleResolver.getInstance(project))
registerResolveComponents()
val provider = FirIdeProvider(
project,
this,
moduleInfo,
scopeProvider,
firFileBuilder,
cache,
project.createDeclarationProvider(searchScope),
project.createPackageProvider(searchScope),
)
register(FirProvider::class, provider)
register(FirIdeProvider::class, provider)
register(FirPhaseManager::class, firPhaseManager)
@OptIn(ExperimentalStdlibApi::class)
val dependentProviders = buildList {
add(
createLibrarySession(
moduleInfo,
project,
builtinsAndCloneableSession,
builtinTypes,
librariesCache,
languageVersionSettings = languageVersionSettings,
configureSession = configureSession,
).symbolProvider
)
dependentModules
.mapTo(this) {
createSourcesSession(
project,
configurator,
it,
builtinsAndCloneableSession,
firPhaseRunner,
sessionInvalidator,
builtinTypes,
sessionsCache,
isRootModule = false,
librariesCache,
configureSession = configureSession,
).symbolProvider
}
}
val dependencyProvider = DependentModuleProviders(this, dependentProviders)
register(
FirSymbolProvider::class,
FirModuleWithDependenciesSymbolProvider(
this,
providers = listOf(
provider.symbolProvider,
JavaSymbolProvider(this, moduleData, project.createJavaClassFinder(searchScope)),
),
dependencyProvider
)
)
register(FirDependenciesSymbolProvider::class, dependencyProvider)
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
registerJavaSpecificResolveComponents()
FirSessionFactory.FirSessionConfigurator(this).apply {
if (isRootModule) {
registerExtendedCommonCheckers()
}
}.configure()
configureSession?.invoke(this)
project.stateConfigurator.configureSourceSession(this)
}
}
fun createLibrarySession(
mainModuleInfo: ModuleSourceInfoBase,
project: Project,
builtinsAndCloneableSession: FirIdeBuiltinsAndCloneableSession,
builtinTypes: BuiltinTypes,
librariesCache: LibrariesCache,
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
configureSession: (FirIdeSession.() -> Unit)?,
): FirIdeLibrariesSession = librariesCache.cached(mainModuleInfo) {
checkCanceled()
val searchScope = project.stateConfigurator.createScopeForModuleLibraries(mainModuleInfo)
FirIdeLibrariesSession(project, searchScope, builtinTypes).apply session@{
registerIdeComponents(project)
register(FirPhaseManager::class, FirPhaseCheckingPhaseManager)
registerCommonComponents(languageVersionSettings)
registerCommonJavaComponents(JavaModuleResolver.getInstance(project))
registerJavaSpecificResolveComponents()
val kotlinSymbolProvider = KotlinDeserializedJvmSymbolsProvider(
this@session,
moduleDataProvider = project.stateConfigurator.createModuleDataProvider(mainModuleInfo).apply {
allModuleData.forEach { it.bindSession(this@session) }
},
kotlinScopeProvider = FirKotlinScopeProvider(::wrapScopeWithJvmMapped),
packagePartProvider = project.stateConfigurator.createPackagePartsProvider(mainModuleInfo, searchScope),
kotlinClassFinder = VirtualFileFinderFactory.getInstance(project).create(searchScope),
javaClassFinder = project.createJavaClassFinder(searchScope)
)
val symbolProvider = FirCompositeSymbolProvider(this, listOf(kotlinSymbolProvider, builtinsAndCloneableSession.symbolProvider))
register(FirProvider::class, FirIdeLibrariesSessionProvider(symbolProvider))
register(FirSymbolProvider::class, symbolProvider)
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
configureSession?.invoke(this)
}
}
fun createBuiltinsAndCloneableSession(
project: Project,
builtinTypes: BuiltinTypes,
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
configureSession: (FirIdeSession.() -> Unit)? = null,
): FirIdeBuiltinsAndCloneableSession {
return FirIdeBuiltinsAndCloneableSession(project, builtinTypes).apply session@{
val moduleData = FirModuleDataImpl(
Name.special("<builtins module>"),
emptyList(),
emptyList(),
emptyList(),
JvmPlatforms.unspecifiedJvmPlatform,
JvmPlatformAnalyzerServices
).apply {
bindSession(this@session)
}
registerIdeComponents(project)
register(FirPhaseManager::class, FirPhaseCheckingPhaseManager)
registerCommonComponents(languageVersionSettings)
val kotlinScopeProvider = FirKotlinScopeProvider(::wrapScopeWithJvmMapped)
val symbolProvider = FirCompositeSymbolProvider(
this,
listOf(
FirIdeBuiltinSymbolProvider(this, moduleData, kotlinScopeProvider),
FirCloneableSymbolProvider(this, moduleData, kotlinScopeProvider),
)
)
register(FirSymbolProvider::class, symbolProvider)
register(FirProvider::class, FirIdeBuiltinsAndCloneableSessionProvider(symbolProvider))
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
configureSession?.invoke(this)
}
}
private fun FirIdeSession.registerIdeComponents(project: Project) {
register(IdeSessionComponents::class, IdeSessionComponents.create(this))
register(FirCachesFactory::class, FirThreadSafeCachesFactory)
register(SealedClassInheritorsProvider::class, project.stateConfigurator.createSealedInheritorsProvider())
}
}
@Deprecated(
"This is a dirty hack used only for one usage (building fir for psi from stubs) and it should be removed after fix of that usage",
level = DeprecationLevel.ERROR
)
@OptIn(PrivateSessionConstructor::class)
fun createEmptySession(): FirSession {
return object : FirSession(null, Kind.Source) {}.apply {
val moduleData = FirModuleDataImpl(
Name.identifier("<stub module>"),
dependencies = emptyList(),
dependsOnDependencies = emptyList(),
friendDependencies = emptyList(),
platform = JvmPlatforms.unspecifiedJvmPlatform,
analyzerServices = JvmPlatformAnalyzerServices
)
registerModuleData(moduleData)
moduleData.bindSession(this)
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.Immutable
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
@Immutable
class FirIdeSessionProvider internal constructor(
val project: Project,
internal val rootModuleSession: FirIdeSourcesSession,
val sessions: Map<ModuleSourceInfoBase, FirIdeSession>
) : FirSessionProvider() {
override fun getSession(moduleData: FirModuleData): FirSession? =
sessions[moduleData.moduleSourceInfo]
fun getSession(moduleInfo: ModuleInfo): FirSession? =
sessions[moduleInfo]
internal fun getModuleCache(moduleSourceInfo: ModuleSourceInfoBase): ModuleFileCache =
(sessions.getValue(moduleSourceInfo) as FirIdeSourcesSession).cache
}
@@ -0,0 +1,151 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toPersistentMap
import org.jetbrains.kotlin.analysis.providers.createLibrariesModificationTracker
import org.jetbrains.kotlin.analysis.providers.createModuleWithoutDependenciesOutOfBlockModificationTracker
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import org.jetbrains.kotlin.fir.BuiltinTypes
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.session.FirModuleInfoBasedModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveStateConfigurator
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.addValueFor
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.executeWithoutPCE
import java.util.concurrent.ConcurrentHashMap
class FirIdeSessionProviderStorage(val project: Project) {
private val sessionsCache = ConcurrentHashMap<ModuleSourceInfoBase, FromModuleViewSessionCache>()
private val configurator = ServiceManager.getService(project, FirModuleResolveStateConfigurator::class.java)
private val librariesCache by cachedValue(project, project.createLibrariesModificationTracker()) { LibrariesCache() }
fun getSessionProvider(
rootModule: ModuleSourceInfoBase,
configureSession: (FirIdeSession.() -> Unit)? = null
): FirIdeSessionProvider {
val firPhaseRunner = FirPhaseRunner()
val builtinTypes = BuiltinTypes()
val builtinsAndCloneableSession = FirIdeSessionFactory.createBuiltinsAndCloneableSession(project, builtinTypes)
val cache = sessionsCache.getOrPut(rootModule) { FromModuleViewSessionCache(rootModule) }
val (sessions, session) = cache.withMappings(project) { mappings ->
val sessions = mutableMapOf<ModuleSourceInfoBase, FirIdeSourcesSession>().apply { putAll(mappings) }
val session = executeWithoutPCE {
FirIdeSessionFactory.createSourcesSession(
project,
configurator,
rootModule,
builtinsAndCloneableSession,
firPhaseRunner,
cache.sessionInvalidator,
builtinTypes,
sessions,
isRootModule = true,
librariesCache,
configureSession = configureSession,
)
}
sessions to session
}
return FirIdeSessionProvider(project, session, sessions)
}
}
private class FromModuleViewSessionCache(
val root: ModuleSourceInfoBase,
) {
@Volatile
private var mappings: PersistentMap<ModuleSourceInfoBase, FirSessionWithModificationTracker> = persistentMapOf()
val sessionInvalidator: FirSessionInvalidator = FirSessionInvalidator { session ->
mappings[session.moduleData.moduleSourceInfo]?.invalidate()
}
inline fun <R> withMappings(
project: Project,
action: (Map<ModuleSourceInfoBase, FirIdeSourcesSession>) -> Pair<Map<ModuleSourceInfoBase, FirIdeSourcesSession>, R>
): Pair<Map<ModuleSourceInfoBase, FirIdeSourcesSession>, R> {
val (newMappings, result) = action(getSessions().mapValues { it.value })
mappings = newMappings.mapValues { FirSessionWithModificationTracker(project, it.value) }.toPersistentMap()
return newMappings to result
}
@OptIn(ExperimentalStdlibApi::class)
private fun getSessions(): Map<ModuleSourceInfoBase, FirIdeSourcesSession> = buildMap {
val sessions = mappings.values
val wasSessionInvalidated = sessions.associateWithTo(hashMapOf()) { false }
val reversedDependencies = sessions.reversedDependencies { session ->
session.firSession.dependencies.mapNotNull { mappings[it] }
}
fun markAsInvalidWithDfs(session: FirSessionWithModificationTracker) {
if (wasSessionInvalidated.getValue(session)) {
// we already was in that branch
return
}
wasSessionInvalidated[session] = true
reversedDependencies[session]?.forEach { dependsOn ->
markAsInvalidWithDfs(dependsOn)
}
}
for (session in sessions) {
if (!session.isValid) {
markAsInvalidWithDfs(session)
}
}
return wasSessionInvalidated.entries
.mapNotNull { (session, wasInvalidated) -> session.takeUnless { wasInvalidated } }
.associate { session -> session.firSession.moduleData.moduleSourceInfo to session.firSession }
}
private fun <T> Collection<T>.reversedDependencies(getDependencies: (T) -> List<T>): Map<T, List<T>> {
val result = hashMapOf<T, MutableList<T>>()
forEach { from ->
getDependencies(from).forEach { to ->
result.addValueFor(to, from)
}
}
return result
}
}
private class FirSessionWithModificationTracker(
project: Project,
val firSession: FirIdeSourcesSession,
) {
private val modificationTracker =
firSession.moduleData.moduleSourceInfo.createModuleWithoutDependenciesOutOfBlockModificationTracker(project)
private val timeStamp = modificationTracker.modificationCount
@Volatile
private var isInvalidated = false
fun invalidate() {
isInvalidated = true
}
val isValid: Boolean get() = !isInvalidated && modificationTracker.modificationCount == timeStamp
}
val FirModuleData.moduleSourceInfo: ModuleSourceInfoBase
get() = moduleInfoUnsafe()
inline fun <reified T : ModuleInfo> FirModuleData.moduleInfoUnsafe(): T = (this as FirModuleInfoBasedModuleData).moduleInfo as T
inline fun <reified T : ModuleInfo> FirModuleData.moduleInfoSafe(): T? = (this as FirModuleInfoBasedModuleData).moduleInfo as? T
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import org.jetbrains.kotlin.fir.BuiltinTypes
import org.jetbrains.kotlin.fir.PrivateSessionConstructor
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firIdeProvider
@OptIn(PrivateSessionConstructor::class)
internal class FirIdeSourcesSession @PrivateSessionConstructor constructor(
val dependencies: List<ModuleSourceInfoBase>,
override val project: Project,
override val scope: GlobalSearchScope,
val firFileBuilder: FirFileBuilder,
builtinTypes: BuiltinTypes,
) : FirIdeModuleSession(builtinTypes, Kind.Source) {
val cache get() = firIdeProvider.cache
}
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import org.jetbrains.kotlin.fir.FirSession
internal class FirSessionInvalidator(private val invalidateSourcesSession: (FirIdeSourcesSession) -> Unit) {
fun invalidate(session: FirSession) {
require(session is FirIdeSourcesSession)
invalidateSourcesSession(session)
}
}
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import java.util.concurrent.ConcurrentHashMap
@JvmInline
internal value class LibrariesCache(private val cache: ConcurrentHashMap<ModuleSourceInfoBase, FirIdeLibrariesSession> = ConcurrentHashMap()) {
fun cached(moduleSourceInfo: ModuleSourceInfoBase, create: (ModuleSourceInfoBase) -> FirIdeLibrariesSession): FirIdeLibrariesSession =
cache.computeIfAbsent(moduleSourceInfo, create)
}
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.FirAnnotationArgumentsResolveTransformer
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ensurePhase
internal class FirDesignatedAnnotationArgumentsResolveTransformerForIDE(
private val designation: FirDeclarationDesignationWithFile,
session: FirSession,
scopeSession: ScopeSession,
) : FirLazyTransformerForIDE, FirAnnotationArgumentsResolveTransformer(session, scopeSession) {
private fun moveNextDeclaration(designationIterator: Iterator<FirDeclaration>) {
if (!designationIterator.hasNext()) {
designation.declaration.transform<FirDeclaration, ResolutionMode>(declarationsTransformer, ResolutionMode.ContextIndependent)
return
}
when (val nextElement = designationIterator.next()) {
is FirFile -> {
context.withFile(nextElement, components) {
moveNextDeclaration(designationIterator)
}
}
is FirRegularClass -> {
context.withContainingClass(nextElement) {
moveNextDeclaration(designationIterator)
}
}
is FirEnumEntry -> {
context.forEnumEntry {
moveNextDeclaration(designationIterator)
}
}
else -> {
error("Unexpected declaration in designation: ${nextElement::class.qualifiedName}")
}
}
}
override fun transformDeclaration(phaseRunner: FirPhaseRunner) {
if (designation.declaration.resolvePhase >= FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS) return
designation.declaration.ensurePhase(FirResolvePhase.STATUS)
val designationIterator = designation.toSequenceWithFile(includeTarget = false).iterator()
ResolveTreeBuilder.resolvePhase(designation.declaration, FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS) {
phaseRunner.runPhaseWithCustomResolve(FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS) {
moveNextDeclaration(designationIterator)
}
}
FirLazyTransformerForIDE.updatePhaseDeep(designation.declaration, FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS)
ensureResolved(designation.declaration)
ensureResolvedDeep(designation.declaration)
}
override fun ensureResolved(declaration: FirDeclaration) {
if (declaration is FirAnnotatedDeclaration) {
val unresolvedAnnotation = declaration.annotations.firstOrNull { it.annotationTypeRef !is FirResolvedTypeRef }
check(unresolvedAnnotation == null) {
"Unexpected annotationTypeRef annotation, expected resolvedType but actual ${unresolvedAnnotation?.annotationTypeRef}"
}
}
when (declaration) {
is FirSimpleFunction, is FirConstructor, is FirAnonymousInitializer ->
declaration.ensurePhase(FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS)
is FirProperty -> {
declaration.ensurePhase(FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS)
// declaration.getter?.ensurePhase(FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS)
// declaration.setter?.ensurePhase(FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS)
}
is FirClass, is FirTypeAlias, is FirEnumEntry, is FirField -> Unit
else -> error("Unexpected type: ${declaration::class.simpleName}")
}
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.FirProviderInterceptor
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.ImplicitBodyResolveComputationSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.createReturnTypeCalculatorForIDE
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirIdeEnsureBasedTransformerForReturnTypeCalculator
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirLazyTransformerForIDE.Companion.updatePhaseDeep
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ensurePhase
/**
* Transform designation into BODY_RESOLVE declaration. Affects only for target declaration and it's children
*/
internal class FirDesignatedBodyResolveTransformerForIDE(
private val designation: FirDeclarationDesignationWithFile,
session: FirSession,
scopeSession: ScopeSession,
towerDataContextCollector: FirTowerDataContextCollector?,
firProviderInterceptor: FirProviderInterceptor?,
) : FirLazyTransformerForIDE, FirBodyResolveTransformer(
session,
phase = FirResolvePhase.BODY_RESOLVE,
implicitTypeOnly = false,
scopeSession = scopeSession,
returnTypeCalculator = createReturnTypeCalculatorForIDE(
session,
scopeSession,
ImplicitBodyResolveComputationSession(),
::FirIdeEnsureBasedTransformerForReturnTypeCalculator
),
firTowerDataContextCollector = towerDataContextCollector,
firProviderInterceptor = firProviderInterceptor,
) {
private val ideDeclarationTransformer = IDEDeclarationTransformer(designation)
override fun transformDeclarationContent(declaration: FirDeclaration, data: ResolutionMode): FirDeclaration =
ideDeclarationTransformer.transformDeclarationContent(this, declaration, data) {
super.transformDeclarationContent(declaration, data)
}
override fun transformDeclaration(phaseRunner: FirPhaseRunner) {
if (designation.declaration.resolvePhase >= FirResolvePhase.BODY_RESOLVE) return
designation.declaration.ensurePhase(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE)
ResolveTreeBuilder.resolvePhase(designation.declaration, FirResolvePhase.BODY_RESOLVE) {
phaseRunner.runPhaseWithCustomResolve(FirResolvePhase.BODY_RESOLVE) {
designation.firFile.transform<FirFile, ResolutionMode>(this, ResolutionMode.ContextIndependent)
}
}
ideDeclarationTransformer.ensureDesignationPassed()
updatePhaseDeep(designation.declaration, FirResolvePhase.BODY_RESOLVE, withNonLocalDeclarations = true)
ensureResolved(designation.declaration)
ensureResolvedDeep(designation.declaration)
}
override fun ensureResolved(declaration: FirDeclaration) {
when (declaration) {
is FirSimpleFunction, is FirConstructor, is FirTypeAlias, is FirField, is FirAnonymousInitializer ->
declaration.ensurePhase(FirResolvePhase.BODY_RESOLVE)
is FirProperty -> {
declaration.ensurePhase(FirResolvePhase.BODY_RESOLVE)
// declaration.getter?.ensurePhase(FirResolvePhase.BODY_RESOLVE)
// declaration.setter?.ensurePhase(FirResolvePhase.BODY_RESOLVE)
}
is FirEnumEntry, is FirClass -> Unit
else -> error("Unexpected type: ${declaration::class.simpleName}")
}
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.contracts.FirContractResolveTransformer
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyBodiesCalculator
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirLazyTransformerForIDE.Companion.updatePhaseDeep
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ensurePhase
/**
* Transform designation into CONTRACTS declaration. Affects only for target declaration and it's children
*/
internal class FirDesignatedContractsResolveTransformerForIDE(
private val designation: FirDeclarationDesignationWithFile,
session: FirSession,
scopeSession: ScopeSession,
) : FirLazyTransformerForIDE, FirContractResolveTransformer(session, scopeSession) {
private val ideDeclarationTransformer = IDEDeclarationTransformer(designation)
override val declarationsTransformer: FirDeclarationsResolveTransformer = object : FirDeclarationsContractResolveTransformer(this) {
override fun transformDeclarationContent(firClass: FirClass, data: ResolutionMode) {
ideDeclarationTransformer.transformDeclarationContent(this, firClass, data) {
super.transformDeclarationContent(firClass, data)
firClass
}
}
}
override fun transformDeclarationContent(declaration: FirDeclaration, data: ResolutionMode): FirDeclaration =
ideDeclarationTransformer.transformDeclarationContent(this, declaration, data) {
super.transformDeclarationContent(declaration, data)
}
override fun transformDeclaration(phaseRunner: FirPhaseRunner) {
if (designation.declaration.resolvePhase >= FirResolvePhase.CONTRACTS) return
designation.declaration.ensurePhase(FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS)
FirLazyBodiesCalculator.calculateLazyBodiesInside(designation)
ResolveTreeBuilder.resolvePhase(designation.declaration, FirResolvePhase.CONTRACTS) {
phaseRunner.runPhaseWithCustomResolve(FirResolvePhase.CONTRACTS) {
designation.firFile.transform<FirFile, ResolutionMode>(this, ResolutionMode.ContextIndependent)
}
}
ideDeclarationTransformer.ensureDesignationPassed()
updatePhaseDeep(designation.declaration, FirResolvePhase.CONTRACTS)
ensureResolved(designation.declaration)
ensureResolvedDeep(designation.declaration)
}
override fun ensureResolved(declaration: FirDeclaration) {
when (declaration) {
is FirSimpleFunction, is FirConstructor, is FirAnonymousInitializer ->
declaration.ensurePhase(FirResolvePhase.CONTRACTS)
is FirProperty -> {
declaration.ensurePhase(FirResolvePhase.CONTRACTS)
// declaration.getter?.ensurePhase(FirResolvePhase.CONTRACTS)
// declaration.setter?.ensurePhase(FirResolvePhase.CONTRACTS)
}
is FirClass, is FirTypeAlias, is FirEnumEntry, is FirField -> Unit
else -> error("Unexpected type: ${declaration::class.simpleName}")
}
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirImplicitAwareBodyResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.ImplicitBodyResolveComputationSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.createReturnTypeCalculatorForIDE
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirIdeDesignatedImpliciteTypesBodyResolveTransformerForReturnTypeCalculator
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirLazyTransformerForIDE.Companion.updatePhaseDeep
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ensurePhase
/**
* Transform designation into IMPLICIT_TYPES_BODY_RESOLVE declaration. Affects only for target declaration, it's children and dependents
*/
internal class FirDesignatedImplicitTypesTransformerForIDE(
private val designation: FirDeclarationDesignationWithFile,
session: FirSession,
scopeSession: ScopeSession,
towerDataContextCollector: FirTowerDataContextCollector?,
implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession = ImplicitBodyResolveComputationSession(),
) : FirLazyTransformerForIDE, FirImplicitAwareBodyResolveTransformer(
session,
implicitBodyResolveComputationSession = implicitBodyResolveComputationSession,
phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
implicitTypeOnly = true,
scopeSession = scopeSession,
firTowerDataContextCollector = towerDataContextCollector,
returnTypeCalculator = createReturnTypeCalculatorForIDE(
session,
scopeSession,
implicitBodyResolveComputationSession,
::FirIdeDesignatedImpliciteTypesBodyResolveTransformerForReturnTypeCalculator
)
) {
private val ideDeclarationTransformer = IDEDeclarationTransformer(designation)
override fun transformDeclarationContent(declaration: FirDeclaration, data: ResolutionMode): FirDeclaration =
ideDeclarationTransformer.transformDeclarationContent(this, declaration, data) {
super.transformDeclarationContent(declaration, data)
}
override fun transformDeclaration(phaseRunner: FirPhaseRunner) {
if (designation.declaration.resolvePhase >= FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) return
designation.declaration.ensurePhase(FirResolvePhase.CONTRACTS)
ResolveTreeBuilder.resolvePhase(designation.declaration, FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) {
phaseRunner.runPhaseWithCustomResolve(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) {
designation.firFile.transform<FirFile, ResolutionMode>(this, ResolutionMode.ContextIndependent)
}
}
ideDeclarationTransformer.ensureDesignationPassed()
updatePhaseDeep(designation.declaration, FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE)
ensureResolved(designation.declaration)
ensureResolvedDeep(designation.declaration)
}
override fun ensureResolved(declaration: FirDeclaration) {
when (declaration) {
is FirSimpleFunction -> check(declaration.returnTypeRef is FirResolvedTypeRef)
is FirField -> check(declaration.returnTypeRef is FirResolvedTypeRef)
is FirClass, is FirConstructor, is FirTypeAlias, is FirEnumEntry, is FirAnonymousInitializer -> Unit
is FirProperty -> {
check(declaration.returnTypeRef is FirResolvedTypeRef)
//Not resolved for some getters and setters #KT-46995
// check(declaration.getter?.returnTypeRef?.let { it is FirResolvedTypeRef } ?: true)
// check(declaration.setter?.returnTypeRef?.let { it is FirResolvedTypeRef } ?: true)
}
else -> error("Unexpected type: ${declaration::class.simpleName}")
}
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.StatusComputationSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirLazyTransformerForIDE.Companion.updatePhaseDeep
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ensurePhase
/**
* Transform designation into STATUS phase. Affects only for designation, target declaration, it's children and dependents
*/
internal class FirDesignatedStatusResolveTransformerForIDE(
private val designation: FirDeclarationDesignationWithFile,
private val session: FirSession,
private val scopeSession: ScopeSession,
) : FirLazyTransformerForIDE {
private inner class FirDesignatedStatusResolveTransformerForIDE :
FirStatusResolveTransformer(session, scopeSession, StatusComputationSession.Regular()) {
val designationTransformer = IDEDeclarationTransformer(designation)
override fun transformDeclarationContent(declaration: FirDeclaration, data: FirResolvedDeclarationStatus?): FirDeclaration =
designationTransformer.transformDeclarationContent(this, declaration, data) {
super.transformDeclarationContent(declaration, data)
}
}
override fun transformDeclaration(phaseRunner: FirPhaseRunner) {
if (designation.declaration.resolvePhase >= FirResolvePhase.STATUS) return
designation.declaration.ensurePhase(FirResolvePhase.TYPES)
val transformer = FirDesignatedStatusResolveTransformerForIDE()
ResolveTreeBuilder.resolvePhase(designation.declaration, FirResolvePhase.STATUS) {
phaseRunner.runPhaseWithCustomResolve(FirResolvePhase.STATUS) {
designation.firFile.transform<FirElement, FirResolvedDeclarationStatus?>(transformer, null)
}
}
transformer.designationTransformer.ensureDesignationPassed()
updatePhaseDeep(designation.declaration, FirResolvePhase.STATUS)
ensureResolved(designation.declaration)
ensureResolvedDeep(designation.declaration)
}
override fun ensureResolved(declaration: FirDeclaration) {
if (declaration !is FirAnonymousInitializer) {
declaration.ensurePhase(FirResolvePhase.STATUS)
}
when (declaration) {
is FirSimpleFunction -> check(declaration.status is FirResolvedDeclarationStatus)
is FirConstructor -> check(declaration.status is FirResolvedDeclarationStatus)
is FirTypeAlias -> check(declaration.status is FirResolvedDeclarationStatus)
is FirEnumEntry -> check(declaration.status is FirResolvedDeclarationStatus)
is FirField -> check(declaration.status is FirResolvedDeclarationStatus)
is FirProperty -> {
check(declaration.status is FirResolvedDeclarationStatus)
check(declaration.getter?.status?.let { it is FirResolvedDeclarationStatus } ?: true)
check(declaration.setter?.status?.let { it is FirResolvedDeclarationStatus } ?: true)
}
is FirRegularClass -> check(declaration.status is FirResolvedDeclarationStatus)
is FirAnonymousInitializer -> Unit
else -> error("Unexpected type: ${declaration::class.simpleName}")
}
}
}
@@ -0,0 +1,174 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.*
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.runCustomResolveUnderLock
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirLazyTransformerForIDE.Companion.updatePhaseDeep
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkCanceled
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ensurePhase
/**
* Transform designation into SUPER_TYPES phase. Affects only for designation, target declaration, it's children and dependents
*/
internal class FirDesignatedSupertypeResolverTransformerForIDE(
private val designation: FirDeclarationDesignationWithFile,
private val session: FirSession,
private val scopeSession: ScopeSession,
private val moduleFileCache: ModuleFileCache,
private val firLazyDeclarationResolver: FirLazyDeclarationResolver,
private val firProviderInterceptor: FirProviderInterceptor?,
private val checkPCE: Boolean,
) : FirLazyTransformerForIDE {
private val supertypeComputationSession = SupertypeComputationSession()
private inner class DesignatedFirSupertypeResolverVisitor(classDesignation: FirDeclarationDesignation) :
FirSupertypeResolverVisitor(
session = session,
supertypeComputationSession = supertypeComputationSession,
scopeSession = scopeSession,
scopeForLocalClass = null,
localClassesNavigationInfo = null,
firProviderInterceptor = firProviderInterceptor,
) {
val declarationTransformer = IDEDeclarationTransformer(classDesignation)
override fun visitDeclarationContent(declaration: FirDeclaration, data: Any?) {
declarationTransformer.visitDeclarationContent(this, declaration, data) {
super.visitDeclarationContent(declaration, data)
declaration
}
}
}
private inner class DesignatedFirApplySupertypesTransformer(classDesignation: FirDeclarationDesignation) :
FirApplySupertypesTransformer(supertypeComputationSession) {
val declarationTransformer = IDEDeclarationTransformer(classDesignation)
override fun transformDeclarationContent(declaration: FirDeclaration, data: Any?): FirDeclaration {
return declarationTransformer.transformDeclarationContent(this, declaration, data) {
super.transformDeclarationContent(declaration, data)
}
}
}
private fun collect(designation: FirDeclarationDesignationWithFile): Collection<FirDeclarationDesignationWithFile> {
val visited = mutableMapOf<FirDeclaration, FirDeclarationDesignationWithFile>()
val toVisit = mutableListOf<FirDeclarationDesignationWithFile>()
toVisit.add(designation)
while (toVisit.isNotEmpty()) {
for (nowVisit in toVisit) {
if (checkPCE) checkCanceled()
val resolver = DesignatedFirSupertypeResolverVisitor(nowVisit)
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(nowVisit.firFile, checkPCE) {
firLazyDeclarationResolver.lazyResolveFileDeclaration(
firFile = nowVisit.firFile,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.IMPORTS,
scopeSession = scopeSession,
checkPCE = true,
)
nowVisit.firFile.accept(resolver, null)
}
resolver.declarationTransformer.ensureDesignationPassed()
visited[nowVisit.declaration] = nowVisit
}
toVisit.clear()
for (value in supertypeComputationSession.supertypeStatusMap.values) {
if (value !is SupertypeComputationStatus.Computed) continue
for (reference in value.supertypeRefs) {
val classLikeDeclaration = reference.type.toSymbol(session)?.fir
if (classLikeDeclaration !is FirClassLikeDeclaration) continue
if (classLikeDeclaration is FirJavaClass) continue
if (visited.containsKey(classLikeDeclaration)) continue
val containingFile = moduleFileCache.getContainerFirFile(classLikeDeclaration) ?: continue
toVisit.add(classLikeDeclaration.collectDesignation(containingFile))
}
}
}
return visited.values
}
private fun apply(visited: Collection<FirDeclarationDesignationWithFile>) {
fun applyToFileSymbols(designations: List<FirDeclarationDesignationWithFile>) {
for (designation in designations) {
if (checkPCE) checkCanceled()
val applier = DesignatedFirApplySupertypesTransformer(designation)
designation.firFile.transform<FirElement, Void?>(applier, null)
applier.declarationTransformer.ensureDesignationPassed()
}
}
val filesToDesignations = visited.groupBy { it.firFile }
for (designationsPerFile in filesToDesignations) {
if (checkPCE) checkCanceled()
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(designationsPerFile.key, checkPCE) {
applyToFileSymbols(designationsPerFile.value)
}
}
}
override fun transformDeclaration(phaseRunner: FirPhaseRunner) {
if (designation.declaration.resolvePhase >= FirResolvePhase.SUPER_TYPES) return
designation.firFile.ensurePhase(FirResolvePhase.IMPORTS)
val targetDesignation = if (designation.declaration !is FirClassLikeDeclaration) {
val resolvableTarget = designation.path.lastOrNull()
if (resolvableTarget == null) {
updatePhaseDeep(designation.declaration, FirResolvePhase.SUPER_TYPES)
return
}
check(resolvableTarget is FirClassLikeDeclaration)
val targetPath = designation.path.dropLast(1)
FirDeclarationDesignationWithFile(targetPath, resolvableTarget, designation.firFile)
} else designation
ResolveTreeBuilder.resolvePhase(designation.declaration, FirResolvePhase.SUPER_TYPES) {
phaseRunner.runPhaseWithCustomResolve(FirResolvePhase.SUPER_TYPES) {
val collected = collect(targetDesignation)
supertypeComputationSession.breakLoops(session)
apply(collected)
}
}
updatePhaseDeep(designation.declaration, FirResolvePhase.SUPER_TYPES)
ensureResolved(designation.declaration)
ensureResolvedDeep(designation.declaration)
}
override fun ensureResolved(declaration: FirDeclaration) {
when (declaration) {
is FirFunction, is FirProperty, is FirEnumEntry, is FirField, is FirAnonymousInitializer -> Unit
is FirRegularClass -> {
declaration.ensurePhase(FirResolvePhase.SUPER_TYPES)
check(declaration.superTypeRefs.all { it is FirResolvedTypeRef })
}
is FirTypeAlias -> {
declaration.ensurePhase(FirResolvePhase.SUPER_TYPES)
check(declaration.expandedTypeRef is FirResolvedTypeRef)
}
else -> error("Unexpected type: ${declaration::class.simpleName}")
}
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.FirTypeResolveTransformer
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirLazyTransformerForIDE.Companion.updatePhaseDeep
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ensurePhase
/**
* Transform designation into TYPES phase. Affects only for designation, target declaration and it's children
*/
internal class FirDesignatedTypeResolverTransformerForIDE(
private val designation: FirDeclarationDesignationWithFile,
session: FirSession,
scopeSession: ScopeSession,
) : FirLazyTransformerForIDE, FirTypeResolveTransformer(session, scopeSession) {
private val declarationTransformer = IDEDeclarationTransformer(designation)
override fun <E : FirElement> transformElement(element: E, data: Any?): E {
return if (element is FirDeclaration && (element is FirRegularClass || element is FirFile)) {
@Suppress("UNCHECKED_CAST")
declarationTransformer.transformDeclarationContent(this, element, data) {
super.transformElement(element, data)
} as E
} else {
super.transformElement(element, data)
}
}
override fun transformDeclaration(phaseRunner: FirPhaseRunner) {
if (designation.declaration.resolvePhase >= FirResolvePhase.TYPES) return
designation.declaration.ensurePhase(FirResolvePhase.SUPER_TYPES)
ResolveTreeBuilder.resolvePhase(designation.declaration, FirResolvePhase.TYPES) {
phaseRunner.runPhaseWithCustomResolve(FirResolvePhase.TYPES) {
designation.firFile.transform<FirFile, Any?>(this, null)
}
}
declarationTransformer.ensureDesignationPassed()
updatePhaseDeep(designation.declaration, FirResolvePhase.TYPES)
ensureResolved(designation.declaration)
ensureResolvedDeep(designation.declaration)
}
override fun ensureResolved(declaration: FirDeclaration) {
if (declaration !is FirAnonymousInitializer) {
declaration.ensurePhase(FirResolvePhase.TYPES)
}
when (declaration) {
is FirFunction -> {
check(declaration.returnTypeRef is FirResolvedTypeRef || declaration.returnTypeRef is FirImplicitTypeRef)
check(declaration.receiverTypeRef?.let { it is FirResolvedTypeRef } ?: true)
declaration.valueParameters.forEach {
check(it.returnTypeRef is FirResolvedTypeRef || it.returnTypeRef is FirImplicitTypeRef)
}
}
is FirProperty -> {
check(declaration.returnTypeRef is FirResolvedTypeRef || declaration.returnTypeRef is FirImplicitTypeRef)
check(declaration.receiverTypeRef?.let { it is FirResolvedTypeRef } ?: true)
declaration.getter?.run(::ensureResolved)
declaration.setter?.run(::ensureResolved)
}
is FirField -> check(declaration.returnTypeRef is FirResolvedTypeRef || declaration.returnTypeRef is FirImplicitTypeRef)
is FirClass, is FirTypeAlias, is FirAnonymousInitializer -> Unit
is FirEnumEntry -> check(declaration.returnTypeRef is FirResolvedTypeRef)
else -> error("Unexpected type: ${declaration::class.simpleName}")
}
}
}
@@ -0,0 +1,60 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.resolved
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.ImplicitBodyResolveComputationSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.createReturnTypeCalculatorForIDE
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirIdeDesignatedImpliciteTypesBodyResolveTransformerForReturnTypeCalculator
internal class FirFileAnnotationsResolveTransformer(
private val firFile: FirFile,
private val annotations: List<FirAnnotation>,
session: FirSession,
scopeSession: ScopeSession,
implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession = ImplicitBodyResolveComputationSession(),
firTowerDataContextCollector: FirTowerDataContextCollector? = null,
) : FirBodyResolveTransformer(
session = session,
phase = FirResolvePhase.BODY_RESOLVE,
implicitTypeOnly = false,
scopeSession = scopeSession,
returnTypeCalculator = createReturnTypeCalculatorForIDE(
session,
scopeSession,
implicitBodyResolveComputationSession,
::FirIdeDesignatedImpliciteTypesBodyResolveTransformerForReturnTypeCalculator
),
firTowerDataContextCollector = firTowerDataContextCollector
), FirLazyTransformerForIDE {
override fun transformDeclarationContent(declaration: FirDeclaration, data: ResolutionMode): FirDeclaration {
require(declaration is FirFile) { "Unexpected declaration ${declaration::class.simpleName}" }
annotations.forEach {
if (!it.resolved) {
it.visitNoTransform(this, data)
}
}
return declaration
}
override fun transformDeclaration(phaseRunner: FirPhaseRunner) {
if (annotations.all { it.resolved }) return
check(firFile.resolvePhase >= FirResolvePhase.IMPORTS) { "Invalid file resolve phase ${firFile.resolvePhase}" }
firFile.accept(this, ResolutionMode.ContextDependent)
check(annotations.all { it.resolved }) {
"Annotation was not resolved"
}
}
override fun ensureResolved(declaration: FirDeclaration) = error("Not implemented")
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignation
internal interface FirLazyTransformerForIDE {
fun transformDeclaration(phaseRunner: FirPhaseRunner)
fun ensureResolved(declaration: FirDeclaration)
fun ensureResolvedDeep(declaration: FirDeclaration) {
if (!enableDeepEnsure) return
ensureResolved(declaration)
if (declaration is FirRegularClass) {
declaration.declarations.forEach(::ensureResolvedDeep)
}
}
companion object {
private object WholeTreePhaseUpdater : FirVisitor<Unit, FirResolvePhase>() {
override fun visitElement(element: FirElement, data: FirResolvePhase) {
if (element is FirDeclaration) {
if (element.resolvePhase >= data && element !is FirDefaultPropertyAccessor) return
element.replaceResolvePhase(data)
}
element.acceptChildren(this, data)
}
}
private fun updatePhaseForNonLocals(element: FirDeclaration, newPhase: FirResolvePhase) {
if (element.resolvePhase >= newPhase) return
element.replaceResolvePhase(newPhase)
when (element) {
is FirProperty -> {
element.getter?.run { if (resolvePhase < newPhase) replaceResolvePhase(newPhase) }
element.setter?.run { if (resolvePhase < newPhase) replaceResolvePhase(newPhase) }
}
is FirClass -> {
element.declarations.forEach {
updatePhaseForNonLocals(it, newPhase)
}
}
else -> Unit
}
}
fun updatePhaseDeep(element: FirDeclaration, newPhase: FirResolvePhase, withNonLocalDeclarations: Boolean = false) {
if (withNonLocalDeclarations) {
WholeTreePhaseUpdater.visitElement(element, newPhase)
} else {
updatePhaseForNonLocals(element, newPhase)
}
}
internal var enableDeepEnsure: Boolean = false
@TestOnly set
val DUMMY = object : FirLazyTransformerForIDE {
override fun transformDeclaration(phaseRunner: FirPhaseRunner) = Unit
override fun ensureResolved(declaration: FirDeclaration) = error("Not implemented")
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.transformers.FirProviderInterceptor
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.name.ClassId
internal class FirProviderInterceptorForIDE private constructor(
private val firFile: FirFile,
private val session: FirSession,
private val symbolSet: Set<FirClassLikeSymbol<*>>,
private val classIdToElementMap: Map<ClassId, FirClassLikeDeclaration>
) : FirProviderInterceptor {
override fun getFirClassifierContainerFileIfAny(symbol: FirClassLikeSymbol<*>): FirFile? =
if (symbolSet.contains(symbol)) firFile else session.firProvider.getFirClassifierContainerFileIfAny(symbol)
override fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration? =
classIdToElementMap[classId] ?: session.firProvider.getFirClassifierByFqName(classId)
companion object {
fun createForFirElement(session: FirSession, firFile: FirFile, element: FirElement): FirProviderInterceptor {
val nodeInfoCollector = object : FirVisitorVoid() {
val symbolSet = mutableSetOf<FirClassLikeSymbol<*>>()
val classIdToElementMap = mutableMapOf<ClassId, FirClassLikeDeclaration>()
override fun visitElement(element: FirElement) {
if (element is FirClassLikeDeclaration) {
symbolSet.add(element.symbol)
classIdToElementMap[element.symbol.classId] = element
}
element.acceptChildren(this)
}
}
nodeInfoCollector.visitElement(element)
return FirProviderInterceptorForIDE(firFile, session, nodeInfoCollector.symbolSet, nodeInfoCollector.classIdToElementMap)
}
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignation
internal class IDEDeclarationTransformer(private val designation: FirDeclarationDesignation) {
private val designationWithoutTargetIterator = designation.toSequence(includeTarget = false).iterator()
private var isInsideTargetDeclaration: Boolean = false
private var designationPassed: Boolean = false
inline fun <D> visitDeclarationContent(
visitor: FirVisitor<Unit, D>,
declaration: FirDeclaration,
data: D,
default: () -> FirDeclaration
): FirDeclaration = processDeclarationContent(declaration, default) {
it.accept(visitor, data)
}
inline fun <D> transformDeclarationContent(
transformer: FirDefaultTransformer<D>,
declaration: FirDeclaration,
data: D,
default: () -> FirDeclaration
): FirDeclaration = processDeclarationContent(declaration, default) { toTransform ->
toTransform.transform<FirElement, D>(transformer, data).also { transformed ->
check(transformed === toTransform) {
"become $transformed `${transformed.render()}`, was ${toTransform}: `${toTransform.render()}`"
}
}
}
private inline fun processDeclarationContent(
declaration: FirDeclaration,
default: () -> FirDeclaration,
applyToDesignated: (FirDeclaration) -> Unit,
): FirDeclaration {
//It means that we are inside the target declaration
if (isInsideTargetDeclaration) {
return default()
}
//It means that we already transform target declaration and now can skip all others
if (designationPassed) {
return declaration
}
if (designationWithoutTargetIterator.hasNext()) {
applyToDesignated(designationWithoutTargetIterator.next())
} else {
try {
isInsideTargetDeclaration = true
designationPassed = true
applyToDesignated(designation.declaration)
} finally {
isInsideTargetDeclaration = false
}
}
return declaration
}
fun ensureDesignationPassed() {
check(designationPassed) { "Designation not passed for declaration ${designation.declaration::class.simpleName}" }
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.FirProviderInterceptor
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyDeclarationResolver
internal object LazyTransformerFactory {
fun createLazyTransformer(
phase: FirResolvePhase,
designation: FirDeclarationDesignationWithFile,
scopeSession: ScopeSession,
moduleFileCache: ModuleFileCache,
lazyDeclarationResolver: FirLazyDeclarationResolver,
towerDataContextCollector: FirTowerDataContextCollector?,
firProviderInterceptor: FirProviderInterceptor?,
checkPCE: Boolean,
): FirLazyTransformerForIDE = when (phase) {
FirResolvePhase.SEALED_CLASS_INHERITORS -> FirLazyTransformerForIDE.DUMMY
FirResolvePhase.SUPER_TYPES -> FirDesignatedSupertypeResolverTransformerForIDE(
designation = designation,
session = designation.firFile.moduleData.session,
scopeSession = scopeSession,
moduleFileCache = moduleFileCache,
firLazyDeclarationResolver = lazyDeclarationResolver,
firProviderInterceptor = firProviderInterceptor,
checkPCE = checkPCE,
)
FirResolvePhase.TYPES -> FirDesignatedTypeResolverTransformerForIDE(
designation,
designation.firFile.moduleData.session,
scopeSession,
)
FirResolvePhase.STATUS -> FirDesignatedStatusResolveTransformerForIDE(
designation,
designation.firFile.moduleData.session,
scopeSession,
)
FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS -> FirDesignatedAnnotationArgumentsResolveTransformerForIDE(
designation,
designation.firFile.moduleData.session,
scopeSession,
)
FirResolvePhase.CONTRACTS -> FirDesignatedContractsResolveTransformerForIDE(
designation,
designation.firFile.moduleData.session,
scopeSession,
)
FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE -> FirDesignatedImplicitTypesTransformerForIDE(
designation,
designation.firFile.moduleData.session,
scopeSession,
towerDataContextCollector
)
FirResolvePhase.BODY_RESOLVE -> FirDesignatedBodyResolveTransformerForIDE(
designation,
designation.firFile.moduleData.session,
scopeSession,
towerDataContextCollector,
firProviderInterceptor,
)
else -> error("Non-lazy phase $phase")
}
}

Some files were not shown because too many files have changed in this diff Show More