FIR IDE: move low level api main sources to the analysis directory
This commit is contained in:
@@ -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()
|
||||
|
||||
+76
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+74
@@ -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
|
||||
)
|
||||
|
||||
|
||||
+7
@@ -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
|
||||
|
||||
+7
@@ -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
|
||||
|
||||
+95
@@ -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)
|
||||
}
|
||||
+168
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
+24
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -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()
|
||||
+12
@@ -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
|
||||
+15
@@ -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
|
||||
+90
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+11
@@ -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,
|
||||
}
|
||||
+12
@@ -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),
|
||||
}
|
||||
+109
@@ -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)
|
||||
}
|
||||
+75
@@ -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
|
||||
}
|
||||
+37
@@ -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)
|
||||
+43
@@ -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())
|
||||
}
|
||||
+209
@@ -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")
|
||||
}
|
||||
}
|
||||
+223
@@ -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)
|
||||
+344
@@ -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)
|
||||
}
|
||||
+19
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+19
@@ -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
|
||||
}
|
||||
+21
@@ -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()
|
||||
}
|
||||
+29
@@ -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()
|
||||
}
|
||||
+19
@@ -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)
|
||||
}
|
||||
+24
@@ -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()
|
||||
}
|
||||
+107
@@ -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
|
||||
}
|
||||
+137
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -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()
|
||||
+30
@@ -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)
|
||||
}
|
||||
}
|
||||
+17
@@ -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)
|
||||
}
|
||||
+122
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -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)
|
||||
}
|
||||
}
|
||||
+74
@@ -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}")
|
||||
}
|
||||
}
|
||||
+35
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -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)
|
||||
}
|
||||
}
|
||||
+24
@@ -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)
|
||||
}
|
||||
}
|
||||
+25
@@ -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)
|
||||
}
|
||||
}
|
||||
+37
@@ -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
|
||||
}
|
||||
}
|
||||
+153
@@ -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
|
||||
}
|
||||
}
|
||||
+107
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -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)
|
||||
}
|
||||
}
|
||||
+71
@@ -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()
|
||||
}
|
||||
+44
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
@@ -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)
|
||||
}
|
||||
}
|
||||
+72
@@ -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()
|
||||
}
|
||||
+71
@@ -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
|
||||
+153
@@ -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")
|
||||
}
|
||||
}
|
||||
+26
@@ -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)
|
||||
}
|
||||
}
|
||||
+277
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -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
|
||||
|
||||
+7
@@ -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
|
||||
|
||||
+7
@@ -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
|
||||
|
||||
+57
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+107
@@ -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) }
|
||||
}
|
||||
}
|
||||
+7
@@ -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
|
||||
|
||||
+7
@@ -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
|
||||
|
||||
+169
@@ -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
|
||||
}
|
||||
}
|
||||
+151
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+405
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+67
@@ -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}")
|
||||
}
|
||||
}
|
||||
+36
@@ -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
|
||||
}
|
||||
+143
@@ -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)
|
||||
}
|
||||
|
||||
+52
@@ -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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -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()
|
||||
}
|
||||
+154
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -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))
|
||||
}
|
||||
}
|
||||
+36
@@ -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")
|
||||
}
|
||||
+38
@@ -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")
|
||||
}
|
||||
+138
@@ -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()
|
||||
+130
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -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)
|
||||
}
|
||||
+10
@@ -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)
|
||||
+131
@@ -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
|
||||
}
|
||||
+94
@@ -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
|
||||
}
|
||||
}
|
||||
+16
@@ -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)
|
||||
+22
@@ -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)
|
||||
+22
@@ -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
|
||||
}
|
||||
+263
@@ -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)
|
||||
}
|
||||
}
|
||||
+31
@@ -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
|
||||
}
|
||||
+151
@@ -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
|
||||
+26
@@ -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
|
||||
}
|
||||
|
||||
+15
@@ -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)
|
||||
}
|
||||
}
|
||||
+15
@@ -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)
|
||||
}
|
||||
+89
@@ -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}")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+84
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+76
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
+174
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -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")
|
||||
}
|
||||
+72
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -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}" }
|
||||
}
|
||||
}
|
||||
+72
@@ -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
Reference in New Issue
Block a user