FIR IDE: rewrite low level API
- Cache ModuleResolveState for module till the world changes
- Resolve every file under a lock
- All creation of raw fir files and resolve of them happens in FirFileBuilder
- Lazy resolve of fir elements happens in FirElementBuilder
Caching works like the following:
- FirModuleResolveState holds PsiToFirCache & DiagnosticsCollector & FileCacheForModuleProvider
- FileCacheForModuleProvider holds a mapping from ModuleInfo to ModuleFileCache
- ModuleFileCache caches
- KtFile -> FirFile mapping
- ClassId -> FirClassLikeDeclaration, CallableId -> FirCallableSymbol
which used in corresponding FirProvider
- mapping from declaration to it's file
which used in corresponding FirProvider
- locks for fir file resolving
- PsiToFirCache provides mapping from KtElement to FirElement
- DiagnosticsCollector collects diagnostics for file and caches them
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ class FirJavaModuleBasedSession private constructor(
|
|||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
sessionProvider.sessionCache[moduleInfo] = this
|
sessionProvider.registerSession(moduleInfo, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,14 +118,18 @@ class FirLibrarySession private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
sessionProvider.sessionCache[moduleInfo] = this
|
sessionProvider.registerSession(moduleInfo, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FirProjectSessionProvider(override val project: Project) : FirSessionProvider {
|
open class FirProjectSessionProvider(override val project: Project) : FirSessionProvider {
|
||||||
override fun getSession(moduleInfo: ModuleInfo): FirSession? {
|
override fun getSession(moduleInfo: ModuleInfo): FirSession? {
|
||||||
return sessionCache[moduleInfo]
|
return sessionCache[moduleInfo]
|
||||||
}
|
}
|
||||||
|
|
||||||
val sessionCache = mutableMapOf<ModuleInfo, FirSession>()
|
fun registerSession(moduleInfo: ModuleInfo, session: FirSession) {
|
||||||
|
sessionCache[moduleInfo] = session
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open val sessionCache: MutableMap<ModuleInfo, FirSession> = mutableMapOf()
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -55,8 +55,6 @@ abstract class FirSymbolProvider : FirSessionComponent {
|
|||||||
open fun getNestedClassesNamesInClass(classId: ClassId): Set<Name> = emptySet()
|
open fun getNestedClassesNamesInClass(classId: ClassId): Set<Name> = emptySet()
|
||||||
|
|
||||||
abstract fun getPackage(fqName: FqName): FqName? // TODO: Replace to symbol sometime
|
abstract fun getPackage(fqName: FqName): FqName? // TODO: Replace to symbol sometime
|
||||||
|
|
||||||
open fun getAllCallableNamesInPackage(): Set<Name> = emptySet()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FirSession.getNestedClassifierScope(lookupTag: ConeClassLikeLookupTag): FirScope? =
|
fun FirSession.getNestedClassifierScope(lookupTag: ConeClassLikeLookupTag): FirScope? =
|
||||||
|
|||||||
-4
@@ -46,8 +46,4 @@ class FirCompositeSymbolProvider(val providers: List<FirSymbolProvider>) : FirSy
|
|||||||
override fun getNestedClassesNamesInClass(classId: ClassId): Set<Name> {
|
override fun getNestedClassesNamesInClass(classId: ClassId): Set<Name> {
|
||||||
return providers.flatMapTo(mutableSetOf()) { it.getNestedClassesNamesInClass(classId) }
|
return providers.flatMapTo(mutableSetOf()) { it.getNestedClassesNamesInClass(classId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllCallableNamesInPackage(): Set<Name> {
|
|
||||||
return providers.flatMapTo(mutableSetOf()) { it.getAllCallableNamesInPackage() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
-7
@@ -76,12 +76,6 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: KotlinSc
|
|||||||
klass.accept(FirRecorder, state to owner.file)
|
klass.accept(FirRecorder, state to owner.file)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllCallableNamesInPackage(): Set<Name> {
|
|
||||||
return state.callableMap.keys.asSequence()
|
|
||||||
.filter { it.className == null }
|
|
||||||
.mapTo(mutableSetOf()) { it.callableName }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val FirAnnotatedDeclaration.file: FirFile
|
private val FirAnnotatedDeclaration.file: FirFile
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
is FirFile -> this
|
is FirFile -> this
|
||||||
@@ -92,7 +86,6 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: KotlinSc
|
|||||||
private fun recordFile(file: FirFile, state: State) {
|
private fun recordFile(file: FirFile, state: State) {
|
||||||
val packageName = file.packageFqName
|
val packageName = file.packageFqName
|
||||||
state.fileMap.merge(packageName, listOf(file)) { a, b -> a + b }
|
state.fileMap.merge(packageName, listOf(file)) { a, b -> a + b }
|
||||||
|
|
||||||
file.acceptChildren(FirRecorder, state to file)
|
file.acceptChildren(FirRecorder, state to file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -237,7 +237,7 @@ private class FirSupertypeResolverVisitor(
|
|||||||
val outerClassFir = classId.outerClassId?.let(session.firProvider::getFirClassifierByFqName) as? FirRegularClass
|
val outerClassFir = classId.outerClassId?.let(session.firProvider::getFirClassifierByFqName) as? FirRegularClass
|
||||||
prepareScopeForNestedClasses(outerClassFir ?: return ImmutableList.empty())
|
prepareScopeForNestedClasses(outerClassFir ?: return ImmutableList.empty())
|
||||||
}
|
}
|
||||||
else -> prepareFileScopes(session.firProvider.getFirClassifierContainerFile(classId))
|
else -> prepareFileScopes(session.firProvider.getFirClassifierContainerFile(classLikeDeclaration.symbol))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.pushIfNotNull(classLikeDeclaration.typeParametersScope())
|
return result.pushIfNotNull(classLikeDeclaration.typeParametersScope())
|
||||||
|
|||||||
+7
-2
@@ -1,10 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* 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.
|
* 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.fir.resolve.transformers
|
package org.jetbrains.kotlin.fir.resolve.transformers
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
@@ -33,7 +34,11 @@ fun <D> AbstractFirBasedSymbol<D>.phasedFir(
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
?: throw AssertionError("Cannot get container file by symbol: $this (${result.render()})")
|
?: throw AssertionError("Cannot get container file by symbol: $this (${result.render()})")
|
||||||
containingFile.runResolve(toPhase = requiredPhase, fromPhase = availablePhase)
|
|
||||||
|
val resolver = fir.session.phasedFirFileResolver
|
||||||
|
?: error("phasedFirFileResolver should be defined when working with FIR in phased mode")
|
||||||
|
resolver.resolveFile(containingFile, fromPhase = availablePhase, toPhase = requiredPhase)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* 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.fir.resolve.transformers
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.FirSessionComponent
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
|
||||||
|
abstract class PhasedFirFileResolver : FirSessionComponent {
|
||||||
|
abstract fun resolveFile(firFile: FirFile, fromPhase: FirResolvePhase, toPhase: FirResolvePhase)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val FirSession.phasedFirFileResolver: PhasedFirFileResolver? by FirSession.nullableSessionComponentAccessor()
|
||||||
+1
-1
@@ -58,6 +58,6 @@ class FirPackageMemberScope(val fqName: FqName, val session: FirSession) : FirSc
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getCallableNames(): Set<Name> {
|
override fun getCallableNames(): Set<Name> {
|
||||||
return symbolProvider.getAllCallableNamesInPackage()
|
return symbolProvider.getAllCallableNamesInPackage(fqName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.analyzer.ModuleInfo
|
|||||||
import org.jetbrains.kotlin.fir.types.impl.*
|
import org.jetbrains.kotlin.fir.types.impl.*
|
||||||
import org.jetbrains.kotlin.fir.utils.ArrayMapAccessor
|
import org.jetbrains.kotlin.fir.utils.ArrayMapAccessor
|
||||||
import org.jetbrains.kotlin.fir.utils.ComponentArrayOwner
|
import org.jetbrains.kotlin.fir.utils.ComponentArrayOwner
|
||||||
|
import org.jetbrains.kotlin.fir.utils.NullableArrayMapAccessor
|
||||||
import org.jetbrains.kotlin.fir.utils.TypeRegistry
|
import org.jetbrains.kotlin.fir.utils.TypeRegistry
|
||||||
import org.jetbrains.kotlin.utils.Jsr305State
|
import org.jetbrains.kotlin.utils.Jsr305State
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@@ -21,6 +22,10 @@ abstract class FirSession(val sessionProvider: FirSessionProvider?) : ComponentA
|
|||||||
inline fun <reified T : FirSessionComponent> sessionComponentAccessor(): ArrayMapAccessor<FirSessionComponent, FirSessionComponent, T> {
|
inline fun <reified T : FirSessionComponent> sessionComponentAccessor(): ArrayMapAccessor<FirSessionComponent, FirSessionComponent, T> {
|
||||||
return generateAccessor(T::class)
|
return generateAccessor(T::class)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : FirSessionComponent> nullableSessionComponentAccessor(): NullableArrayMapAccessor<FirSessionComponent, FirSessionComponent, T> {
|
||||||
|
return generateNullableAccessor(T::class)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open val moduleInfo: ModuleInfo? get() = null
|
open val moduleInfo: ModuleInfo? get() = null
|
||||||
|
|||||||
+3
-14
@@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.stubindex;
|
package org.jetbrains.kotlin.idea.stubindex;
|
||||||
@@ -28,7 +17,7 @@ import org.jetbrains.kotlin.psi.KtClassOrObject;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
public class KotlinFullClassNameIndex extends StringStubIndexExtension<KtClassOrObject> {
|
public class KotlinFullClassNameIndex extends StringStubIndexExtension<KtClassOrObject> {
|
||||||
private static final StubIndexKey<String, KtClassOrObject> KEY = KotlinIndexUtil.createIndexKey(KotlinFullClassNameIndex.class);
|
public static final StubIndexKey<String, KtClassOrObject> KEY = KotlinIndexUtil.createIndexKey(KotlinFullClassNameIndex.class);
|
||||||
|
|
||||||
private static final KotlinFullClassNameIndex ourInstance = new KotlinFullClassNameIndex();
|
private static final KotlinFullClassNameIndex ourInstance = new KotlinFullClassNameIndex();
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea
|
package org.jetbrains.kotlin.idea
|
||||||
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.DuplicatedFirSourceElementsException
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.DuplicatedFirSourceElementsException
|
||||||
|
|
||||||
fun Throwable.shouldBeRethrown(): Boolean = when (this) {
|
fun Throwable.shouldBeRethrown(): Boolean = when (this) {
|
||||||
is DuplicatedFirSourceElementsException -> true
|
is DuplicatedFirSourceElementsException -> true
|
||||||
|
|||||||
-71
@@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.idea.fir.low.level.api
|
|
||||||
|
|
||||||
import com.intellij.openapi.module.impl.scopes.ModuleScopeProviderImpl
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
|
||||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
|
||||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
|
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
|
||||||
|
|
||||||
internal class FirIdeAllSourceDependenciesSymbolProvider(
|
|
||||||
project: Project,
|
|
||||||
moduleInfo: ModuleSourceInfo,
|
|
||||||
private val sessionProvider: FirIdeSessionProvider,
|
|
||||||
private val firBuilder: FirBuilder,
|
|
||||||
) : FirSymbolProvider() {
|
|
||||||
private val scope = ModuleScopeProviderImpl(moduleInfo.module).moduleWithDependenciesScope
|
|
||||||
private val indexHelper = IndexHelper(project)
|
|
||||||
private val packageExistenceChecker = PackageExistenceCheckerForMultipleModules(project, moduleInfo.dependencies())
|
|
||||||
|
|
||||||
private val cache = PsiToFirCacheImpl()
|
|
||||||
|
|
||||||
override fun getClassLikeSymbolByFqName(classId: ClassId): FirClassLikeSymbol<*>? {
|
|
||||||
return executeOrReturnDefaultValueOnPCE(null) {
|
|
||||||
val ktClass = indexHelper.firstMatchingOrNull(
|
|
||||||
KotlinFullClassNameIndex.KEY,
|
|
||||||
classId.asSingleFqName().asString(),
|
|
||||||
scope
|
|
||||||
) { candidate -> candidate.containingKtFile.packageFqName == classId.packageFqName } ?: return null
|
|
||||||
(ktClass.toFir() as FirClassLikeDeclaration<*>).symbol
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
|
||||||
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
|
|
||||||
val callableId = CallableId(packageFqName, name)
|
|
||||||
return executeOrReturnDefaultValueOnPCE(emptyList()) {
|
|
||||||
buildList {
|
|
||||||
indexHelper.getTopLevelFunctions(callableId, scope).mapTo(this) { (it.toFir() as FirCallableDeclaration<*>).symbol }
|
|
||||||
indexHelper.getTopLevelProperties(callableId, scope).mapTo(this) { (it.toFir() as FirCallableDeclaration<*>).symbol }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtElement.toFir(): FirElement =
|
|
||||||
getOrBuildFir(cache, firBuilder, sessionProvider, FirResolvePhase.RAW_FIR)
|
|
||||||
|
|
||||||
override fun getNestedClassifierScope(classId: ClassId): FirScope? {
|
|
||||||
val classifier = getClassLikeSymbolByFqName(classId) ?: return null
|
|
||||||
return classifier.fir.session.firIdeProvider.getNestedClassifierScope(classId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPackage(fqName: FqName): FqName? =
|
|
||||||
fqName.takeIf(packageExistenceChecker::isPackageExists)
|
|
||||||
}
|
|
||||||
+53
-20
@@ -5,56 +5,89 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.fir.low.level.api
|
package org.jetbrains.kotlin.idea.fir.low.level.api
|
||||||
|
|
||||||
|
import com.intellij.openapi.module.impl.scopes.ModuleWithDependentsScope
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.fir.FirModuleBasedSession
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.registerCheckersComponent
|
import org.jetbrains.kotlin.fir.analysis.registerCheckersComponent
|
||||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
import org.jetbrains.kotlin.fir.extensions.BunchOfRegisteredExtensions
|
||||||
|
import org.jetbrains.kotlin.fir.extensions.extensionService
|
||||||
|
import org.jetbrains.kotlin.fir.extensions.registerExtensions
|
||||||
import org.jetbrains.kotlin.fir.java.JavaSymbolProvider
|
import org.jetbrains.kotlin.fir.java.JavaSymbolProvider
|
||||||
import org.jetbrains.kotlin.fir.registerCommonComponents
|
|
||||||
import org.jetbrains.kotlin.fir.registerResolveComponents
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.jvm.registerJvmCallConflictResolverFactory
|
import org.jetbrains.kotlin.fir.resolve.calls.jvm.registerJvmCallConflictResolverFactory
|
||||||
import org.jetbrains.kotlin.fir.resolve.firProvider
|
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCompositeSymbolProvider
|
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCompositeSymbolProvider
|
||||||
import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped
|
import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.transformers.PhasedFirFileResolver
|
||||||
import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider
|
import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCacheImpl
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeModuleLibraryDependenciesSession
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.collectTransitiveDependenciesWithSelf
|
||||||
|
|
||||||
|
|
||||||
internal class FirIdeJavaModuleBasedSession(
|
internal class FirIdeJavaModuleBasedSession private constructor(
|
||||||
moduleInfo: ModuleInfo,
|
moduleInfo: ModuleInfo,
|
||||||
sessionProvider: FirProjectSessionProvider
|
sessionProvider: FirSessionProvider,
|
||||||
|
val firFileBuilder: FirFileBuilder,
|
||||||
) : FirModuleBasedSession(moduleInfo, sessionProvider) {
|
) : FirModuleBasedSession(moduleInfo, sessionProvider) {
|
||||||
|
val cache get() = firIdeProvider.cache
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* Should be invoked only under a [moduleInfo]-based lock
|
||||||
|
*/
|
||||||
fun create(
|
fun create(
|
||||||
project: Project,
|
project: Project,
|
||||||
moduleInfo: ModuleInfo,
|
moduleInfo: ModuleSourceInfo,
|
||||||
sessionProvider: FirProjectSessionProvider,
|
sessionProvider: FirSessionProvider,
|
||||||
scope: GlobalSearchScope
|
|
||||||
): FirIdeJavaModuleBasedSession {
|
): FirIdeJavaModuleBasedSession {
|
||||||
return FirIdeJavaModuleBasedSession(moduleInfo, sessionProvider).apply {
|
val scopeProvider = KotlinScopeProvider(::wrapScopeWithJvmMapped)
|
||||||
|
val firBuilder = FirFileBuilder(sessionProvider as FirIdeSessionProvider, scopeProvider)
|
||||||
|
return FirIdeJavaModuleBasedSession(moduleInfo, sessionProvider, firBuilder).apply {
|
||||||
|
val cache = ModuleFileCacheImpl(this)
|
||||||
|
val phasedFirFileResolver = IdePhasedFirFileResolver(firBuilder, cache)
|
||||||
|
val dependentModules = moduleInfo.collectTransitiveDependenciesWithSelf().filterIsInstance<ModuleSourceInfo>()
|
||||||
|
val searchScope = ModuleWithDependentsScope(project, dependentModules.map { it.module })
|
||||||
|
|
||||||
registerCommonComponents()
|
registerCommonComponents()
|
||||||
registerResolveComponents()
|
registerResolveComponents()
|
||||||
registerCheckersComponent()
|
registerCheckersComponent()
|
||||||
registerJvmCallConflictResolverFactory()
|
|
||||||
|
|
||||||
val firIdeProvider = FirIdeProvider(project, scope, this, KotlinScopeProvider(::wrapScopeWithJvmMapped))
|
val provider = FirIdeProvider(
|
||||||
|
project,
|
||||||
|
this,
|
||||||
|
moduleInfo,
|
||||||
|
scopeProvider,
|
||||||
|
firFileBuilder,
|
||||||
|
cache,
|
||||||
|
searchScope
|
||||||
|
)
|
||||||
|
|
||||||
register(FirProvider::class, firIdeProvider)
|
register(FirProvider::class, provider)
|
||||||
register(FirIdeProvider::class, firIdeProvider)
|
register(FirIdeProvider::class, provider)
|
||||||
|
|
||||||
|
register(PhasedFirFileResolver::class, phasedFirFileResolver)
|
||||||
|
|
||||||
register(
|
register(
|
||||||
FirSymbolProvider::class,
|
FirSymbolProvider::class,
|
||||||
FirCompositeSymbolProvider(
|
FirCompositeSymbolProvider(
|
||||||
listOf(
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
firProvider,
|
buildList {
|
||||||
JavaSymbolProvider(this, sessionProvider.project, scope),
|
add(provider)
|
||||||
FirIdeModuleDependenciesSymbolProvider(this)
|
add(JavaSymbolProvider(this@apply, sessionProvider.project, searchScope))
|
||||||
)
|
add(FirIdeModuleLibraryDependenciesSession.create(moduleInfo, sessionProvider, project).firSymbolProvider)
|
||||||
|
}
|
||||||
) as FirSymbolProvider
|
) as FirSymbolProvider
|
||||||
)
|
)
|
||||||
|
registerJvmCallConflictResolverFactory()
|
||||||
|
extensionService.registerExtensions(BunchOfRegisteredExtensions.empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -14,18 +14,20 @@ import org.jetbrains.kotlin.fir.resolve.providers.impl.FirDependenciesSymbolProv
|
|||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.project.isLibraryClasses
|
import org.jetbrains.kotlin.idea.caches.project.isLibraryClasses
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.IDEPackagePartProvider
|
import org.jetbrains.kotlin.idea.caches.resolve.IDEPackagePartProvider
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe
|
||||||
|
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
//todo make thread safe
|
||||||
internal class FirIdeModuleLibraryDependenciesSymbolProvider(
|
internal class FirIdeModuleLibraryDependenciesSymbolProvider(
|
||||||
session: FirIdeJavaModuleBasedSession
|
session: FirIdeJavaModuleBasedSession
|
||||||
) : FirDependenciesSymbolProviderImpl(session) {
|
) : FirDependenciesSymbolProviderImpl(session) {
|
||||||
//todo invalidate cache on libraries changed
|
|
||||||
override val dependencyProviders: List<FirSymbolProvider> by lazy {
|
override val dependencyProviders: List<FirSymbolProvider> by lazy {
|
||||||
val moduleInfo = session.moduleInfo
|
val moduleInfo = session.moduleInfo
|
||||||
val sessionProvider = session.sessionProvider ?: error("No session provider found")
|
val sessionProvider = session.sessionProvider ?: error("No session provider found")
|
||||||
val project = sessionProvider.project
|
val project = sessionProvider.project
|
||||||
|
|
||||||
moduleInfo.dependenciesWithoutSelf()
|
val dependencies = moduleInfo.dependenciesWithoutSelf()
|
||||||
.filterIsInstance<IdeaModuleInfo>()
|
.filterIsInstance<IdeaModuleInfo>()
|
||||||
.mapNotNull { dependencyInfo ->
|
.mapNotNull { dependencyInfo ->
|
||||||
val dependencySession = if (dependencyInfo.isLibraryClasses()) {
|
val dependencySession = if (dependencyInfo.isLibraryClasses()) {
|
||||||
@@ -37,6 +39,7 @@ internal class FirIdeModuleLibraryDependenciesSymbolProvider(
|
|||||||
} else null
|
} else null
|
||||||
dependencySession?.firSymbolProvider
|
dependencySession?.firSymbolProvider
|
||||||
}.toList()
|
}.toList()
|
||||||
|
dependencies
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-176
@@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.idea.fir.low.level.api
|
|
||||||
|
|
||||||
import com.intellij.openapi.progress.ProcessCanceledException
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
|
||||||
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
|
||||||
import org.jetbrains.kotlin.fir.psi
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProviderInternals
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
|
|
||||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
|
||||||
import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
|
||||||
import org.jetbrains.kotlin.idea.stubindex.*
|
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
|
||||||
|
|
||||||
internal class FirIdeProvider(
|
|
||||||
val project: Project,
|
|
||||||
val scope: GlobalSearchScope,
|
|
||||||
val session: FirSession,
|
|
||||||
kotlinScopeProvider: KotlinScopeProvider
|
|
||||||
) : FirProvider() {
|
|
||||||
private val cacheProvider = FirProviderImpl(session, kotlinScopeProvider)
|
|
||||||
|
|
||||||
data class FirFileWithStamp(val file: FirFile, val stamp: Long)
|
|
||||||
|
|
||||||
private val files = mutableMapOf<KtFile, FirFileWithStamp>()
|
|
||||||
|
|
||||||
override val isPhasedFirAllowed: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration<*>? {
|
|
||||||
return cacheProvider.getFirClassifierByFqName(classId) ?: run {
|
|
||||||
try {
|
|
||||||
val classes = KotlinFullClassNameIndex.getInstance().get(classId.asSingleFqName().asString(), project, scope)
|
|
||||||
val ktClass = classes.firstOrNull {
|
|
||||||
classId.packageFqName == it.containingKtFile.packageFqName
|
|
||||||
} ?: return null // TODO: what if two of them?
|
|
||||||
|
|
||||||
val ktFile = ktClass.containingKtFile
|
|
||||||
getOrBuildFile(ktFile)
|
|
||||||
|
|
||||||
cacheProvider.getFirClassifierByFqName(classId)
|
|
||||||
} catch (e: ProcessCanceledException) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getOrBuildFile(ktFile: KtFile): FirFile {
|
|
||||||
val modificationStamp = ktFile.modificationStamp
|
|
||||||
files[ktFile]?.let { (firFile, stamp) ->
|
|
||||||
if (stamp == modificationStamp) {
|
|
||||||
return firFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return synchronized(ktFile) {
|
|
||||||
var fileWithStamp = files[ktFile]
|
|
||||||
if (fileWithStamp != null && fileWithStamp.stamp == modificationStamp) {
|
|
||||||
fileWithStamp.file
|
|
||||||
} else {
|
|
||||||
val file = RawFirBuilder(session, cacheProvider.kotlinScopeProvider, stubMode = false).buildFirFile(ktFile)
|
|
||||||
cacheProvider.recordFile(file)
|
|
||||||
fileWithStamp = FirFileWithStamp(file, modificationStamp)
|
|
||||||
files[ktFile] = fileWithStamp
|
|
||||||
file
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun buildFunctionWithBody(ktNamedFunction: KtNamedFunction): FirFunction<*> {
|
|
||||||
return RawFirBuilder(session, cacheProvider.kotlinScopeProvider, stubMode = false).buildFunctionWithBody(ktNamedFunction)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFile(ktFile: KtFile): FirFile? {
|
|
||||||
val (firFile, stamp) = files[ktFile] ?: return null
|
|
||||||
if (stamp == ktFile.modificationStamp) return firFile
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getClassLikeSymbolByFqName(classId: ClassId): FirClassLikeSymbol<*>? {
|
|
||||||
return getFirClassifierByFqName(classId)?.symbol
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
|
|
||||||
val packagePrefix = if (packageFqName.isRoot) "" else "$packageFqName."
|
|
||||||
return try {
|
|
||||||
val topLevelFunctions = KotlinTopLevelFunctionFqnNameIndex.getInstance()["$packagePrefix$name", project, scope]
|
|
||||||
val topLevelProperties = KotlinTopLevelPropertyFqnNameIndex.getInstance()["$packagePrefix$name", project, scope]
|
|
||||||
topLevelFunctions.forEach { getOrBuildFile(it.containingKtFile) }
|
|
||||||
topLevelProperties.forEach { getOrBuildFile(it.containingKtFile) }
|
|
||||||
cacheProvider.getTopLevelCallableSymbols(packageFqName, name)
|
|
||||||
} catch (e: ProcessCanceledException) {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFirClassifierContainerFile(fqName: ClassId): FirFile {
|
|
||||||
getFirClassifierByFqName(fqName) // Necessary to ensure cacheProvider contains this classifier
|
|
||||||
return cacheProvider.getFirClassifierContainerFile(fqName)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFirClassifierContainerFileIfAny(fqName: ClassId): FirFile? {
|
|
||||||
getFirClassifierByFqName(fqName) // Necessary to ensure cacheProvider contains this classifier
|
|
||||||
return cacheProvider.getFirClassifierContainerFileIfAny(fqName)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFirClassifierContainerFile(symbol: FirClassLikeSymbol<*>): FirFile {
|
|
||||||
return getFirClassifierContainerFileIfAny(symbol)
|
|
||||||
?: error("Couldn't find container for ${symbol.classId}")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFirClassifierContainerFileIfAny(symbol: FirClassLikeSymbol<*>): FirFile? {
|
|
||||||
val psi = symbol.fir.source?.psi
|
|
||||||
if (psi is KtElement) {
|
|
||||||
return try {
|
|
||||||
val ktFile = psi.containingKtFile
|
|
||||||
getOrBuildFile(ktFile)
|
|
||||||
} catch (e: ProcessCanceledException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return getFirClassifierContainerFileIfAny(symbol.classId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFirCallableContainerFile(symbol: FirCallableSymbol<*>): FirFile? {
|
|
||||||
return cacheProvider.getFirCallableContainerFile(symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFirFilesByPackage(fqName: FqName): List<FirFile> {
|
|
||||||
return try {
|
|
||||||
val files = KotlinExactPackagesIndex.getInstance()[fqName.asString(), project, scope]
|
|
||||||
files.forEach { getOrBuildFile(it) }
|
|
||||||
cacheProvider.getFirFilesByPackage(fqName)
|
|
||||||
} catch (e: ProcessCanceledException) {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getNestedClassifierScope(classId: ClassId): FirScope? {
|
|
||||||
getFirClassifierByFqName(classId)
|
|
||||||
return cacheProvider.getNestedClassifierScope(classId)
|
|
||||||
}
|
|
||||||
|
|
||||||
@FirProviderInternals
|
|
||||||
override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) {
|
|
||||||
// TODO: check that this implementation is correct
|
|
||||||
cacheProvider.recordGeneratedClass(owner, klass)
|
|
||||||
}
|
|
||||||
|
|
||||||
@FirProviderInternals
|
|
||||||
override fun recordGeneratedMember(owner: FirAnnotatedDeclaration, klass: FirDeclaration) {
|
|
||||||
// TODO: check that this implementation is correct
|
|
||||||
cacheProvider.recordGeneratedMember(owner, klass)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO this should be reworked because [FirIdeProvider] should not have such method
|
|
||||||
override fun getAllCallableNamesInPackage(): Set<Name> {
|
|
||||||
return cacheProvider.getAllCallableNamesInPackage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val FirSession.firIdeProvider: FirIdeProvider by FirSession.sessionComponentAccessor()
|
|
||||||
+24
-51
@@ -5,59 +5,32 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.fir.low.level.api
|
package org.jetbrains.kotlin.idea.fir.low.level.api
|
||||||
|
|
||||||
import com.intellij.openapi.components.ServiceManager
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.ModificationTracker
|
import org.jetbrains.kotlin.idea.util.psiModificationTrackerBasedCachedValue
|
||||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.*
|
||||||
|
|
||||||
|
internal class FirIdeResolveStateService(private val project: Project) {
|
||||||
|
private val stateCache by psiModificationTrackerBasedCachedValue(project) {
|
||||||
|
ConcurrentHashMap<IdeaModuleInfo, FirModuleResolveStateImpl>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createResolveStateFor(moduleInfo: IdeaModuleInfo): FirModuleResolveStateImpl {
|
||||||
|
val sessionProvider = FirIdeSessionProvider(project)
|
||||||
|
val session = FirIdeJavaModuleBasedSession.create(
|
||||||
|
project,
|
||||||
|
moduleInfo as ModuleSourceInfo,
|
||||||
|
sessionProvider,
|
||||||
|
).apply { sessionProvider.registerSession(moduleInfo, this) }
|
||||||
|
|
||||||
|
return FirModuleResolveStateImpl(moduleInfo, session, sessionProvider, session.firFileBuilder, session.cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getResolveState(moduleInfo: IdeaModuleInfo): FirModuleResolveStateImpl =
|
||||||
|
stateCache.getOrPut(moduleInfo) { createResolveStateFor(moduleInfo) }
|
||||||
|
|
||||||
internal interface FirIdeResolveStateService {
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getInstance(project: Project): FirIdeResolveStateService =
|
fun getInstance(project: Project): FirIdeResolveStateService = project.service()
|
||||||
ServiceManager.getService(project, FirIdeResolveStateService::class.java)!!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val fallbackModificationTracker: ModificationTracker?
|
|
||||||
|
|
||||||
fun getResolveState(moduleInfo: IdeaModuleInfo): FirModuleResolveStateImpl
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FirModuleData(val state: FirModuleResolveStateImpl, val modificationTracker: ModificationTracker?) {
|
|
||||||
val modificationCount: Long = modificationTracker?.modificationCount ?: Long.MIN_VALUE
|
|
||||||
|
|
||||||
fun isOutOfDate(): Boolean {
|
|
||||||
val currentModCount = modificationTracker?.modificationCount
|
|
||||||
return currentModCount != null && currentModCount > modificationCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class FirIdeResolveStateServiceImpl(val project: Project) : FirIdeResolveStateService {
|
|
||||||
private val stateCache = mutableMapOf<IdeaModuleInfo, FirModuleData>()
|
|
||||||
|
|
||||||
private fun createResolveState(): FirModuleResolveStateImpl {
|
|
||||||
val provider = FirProjectSessionProvider(project)
|
|
||||||
return FirModuleResolveStateImpl(provider)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createModuleData(): FirModuleData {
|
|
||||||
val state = createResolveState()
|
|
||||||
// We want to invalidate cache on every PSI change for now
|
|
||||||
// This is needed for working with high level API until the proper caching is implemented
|
|
||||||
return FirModuleData(state, fallbackModificationTracker)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: multi thread protection
|
|
||||||
override fun getResolveState(moduleInfo: IdeaModuleInfo): FirModuleResolveStateImpl {
|
|
||||||
var moduleData = stateCache.getOrPut(moduleInfo) {
|
|
||||||
createModuleData()
|
|
||||||
}
|
|
||||||
if (moduleData.isOutOfDate()) {
|
|
||||||
moduleData = createModuleData()
|
|
||||||
stateCache[moduleInfo] = moduleData
|
|
||||||
}
|
|
||||||
return moduleData.state
|
|
||||||
}
|
|
||||||
|
|
||||||
override val fallbackModificationTracker: ModificationTracker? =
|
|
||||||
org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService.getInstance(project).modificationTracker
|
|
||||||
}
|
}
|
||||||
+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.idea.fir.low.level.api
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
internal class FirIdeSessionProvider(project: Project) : FirProjectSessionProvider(project) {
|
||||||
|
override val sessionCache = ConcurrentHashMap<ModuleInfo, FirSession>()
|
||||||
|
|
||||||
|
fun getSession(psi: KtElement): FirSession {
|
||||||
|
val moduleInfo = psi.getModuleInfo()
|
||||||
|
return getSession(moduleInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSession(moduleInfo: ModuleInfo): FirSession {
|
||||||
|
return sessionCache[moduleInfo]
|
||||||
|
?: error("$moduleInfo")
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
-166
@@ -5,196 +5,89 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.fir.low.level.api
|
package org.jetbrains.kotlin.idea.fir.low.level.api
|
||||||
|
|
||||||
import com.google.common.collect.MapMaker
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiElementVisitor
|
|
||||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
|
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
import org.jetbrains.kotlin.fir.extensions.BunchOfRegisteredExtensions
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.extensions.extensionService
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
import org.jetbrains.kotlin.fir.extensions.registerExtensions
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
||||||
import org.jetbrains.kotlin.fir.render
|
|
||||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
|
||||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
|
||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.project.LibrarySourceInfo
|
|
||||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
|
||||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.DiagnosticsCollector
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirElementBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.PsiToFirCache
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
|
||||||
|
|
||||||
abstract class FirModuleResolveState {
|
abstract class FirModuleResolveState {
|
||||||
internal abstract val sessionProvider: FirProjectSessionProvider
|
abstract val moduleInfo: IdeaModuleInfo
|
||||||
|
abstract val firSession: FirSession
|
||||||
|
|
||||||
internal fun getSession(psi: KtElement): FirSession {
|
abstract fun getSessionFor(moduleInfo: IdeaModuleInfo): FirSession
|
||||||
val moduleInfo = psi.getModuleInfo()
|
|
||||||
return getSession(psi.project, moduleInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun getSession(project: Project, moduleInfo: IdeaModuleInfo): FirSession {
|
abstract fun getOrBuildFirFor(element: KtElement, toPhase: FirResolvePhase): FirElement
|
||||||
sessionProvider.getSession(moduleInfo)?.let { return it }
|
|
||||||
val lock = when (moduleInfo) {
|
|
||||||
is ModuleSourceInfo -> moduleInfo.module
|
|
||||||
is LibrarySourceInfo -> moduleInfo.library
|
|
||||||
else -> TODO(moduleInfo.toString())
|
|
||||||
}
|
|
||||||
return synchronized(lock) {
|
|
||||||
val session = sessionProvider.getSession(moduleInfo) ?: FirIdeJavaModuleBasedSession.create(
|
|
||||||
project, moduleInfo, sessionProvider, moduleInfo.contentScope()
|
|
||||||
).also { moduleBasedSession ->
|
|
||||||
sessionProvider.sessionCache[moduleInfo] = moduleBasedSession
|
|
||||||
}
|
|
||||||
session.also {
|
|
||||||
it.extensionService.registerExtensions(BunchOfRegisteredExtensions.empty())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal abstract fun getCachedMapping(psi: KtElement): FirElement?
|
abstract fun getDiagnostics(element: KtElement): List<Diagnostic>
|
||||||
|
|
||||||
internal abstract fun getDiagnostics(psi: KtElement): List<Diagnostic>
|
// todo temporary, used only in completion
|
||||||
|
abstract fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile)
|
||||||
|
|
||||||
internal abstract fun hasDiagnosticsForFile(file: KtFile): Boolean
|
// todo temporary, used only in completion
|
||||||
|
abstract fun getCachedMappingForCompletion(element: KtElement): FirElement?
|
||||||
|
|
||||||
internal abstract fun record(psi: KtElement, fir: FirElement)
|
// todo temporary, used only in completion
|
||||||
|
internal abstract fun lazyResolveFunctionForCompletion(
|
||||||
|
firFunction: FirFunction<*>,
|
||||||
|
containerFirFile: FirFile,
|
||||||
|
firIdeProvider: FirIdeProvider,
|
||||||
|
toPhase: FirResolvePhase,
|
||||||
|
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>
|
||||||
|
)
|
||||||
|
|
||||||
internal abstract fun record(psi: KtElement, diagnostic: Diagnostic)
|
|
||||||
|
|
||||||
internal abstract fun setDiagnosticsForFile(file: KtFile, fir: FirFile, diagnostics: Iterable<FirDiagnostic<*>> = emptyList())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class PsiToFirCache {
|
|
||||||
abstract operator fun get(psi: KtElement): FirElement?
|
|
||||||
abstract operator fun set(psi: KtElement, fir: FirElement)
|
|
||||||
abstract fun remove(psi: PsiElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class PsiToFirCacheImpl : PsiToFirCache() {
|
internal open class FirModuleResolveStateImpl(
|
||||||
private val cache = mutableMapOf<KtElement, FirElement>()
|
override val moduleInfo: IdeaModuleInfo,
|
||||||
|
override val firSession: FirSession,
|
||||||
|
private val sessionProvider: FirIdeSessionProvider,
|
||||||
|
val firFileBuilder: FirFileBuilder,
|
||||||
|
val fileCache: ModuleFileCache,
|
||||||
|
) : FirModuleResolveState() {
|
||||||
|
val psiToFirCache = PsiToFirCache(fileCache)
|
||||||
|
val elementBuilder = FirElementBuilder(firFileBuilder)
|
||||||
|
private val diagnosticsCollector = DiagnosticsCollector(firFileBuilder, fileCache)
|
||||||
|
|
||||||
override fun get(psi: KtElement): FirElement? = cache[psi]
|
override fun getSessionFor(moduleInfo: IdeaModuleInfo): FirSession =
|
||||||
|
sessionProvider.getSession(moduleInfo)
|
||||||
|
|
||||||
override fun set(psi: KtElement, fir: FirElement) {
|
override fun getOrBuildFirFor(element: KtElement, toPhase: FirResolvePhase): FirElement =
|
||||||
cache[psi] = fir
|
elementBuilder.getOrBuildFirFor(element, fileCache, psiToFirCache, toPhase)
|
||||||
|
|
||||||
|
override fun getDiagnostics(element: KtElement): List<Diagnostic> =
|
||||||
|
diagnosticsCollector.getDiagnosticsFor(element)
|
||||||
|
|
||||||
|
override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
|
||||||
|
psiToFirCache.recordElementsForCompletionFrom(fir, firFile, ktFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun remove(psi: PsiElement) {
|
override fun getCachedMappingForCompletion(element: KtElement): FirElement? =
|
||||||
cache.remove(psi)
|
psiToFirCache.getCachedMapping(element)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class FirModuleResolveStateImpl(override val sessionProvider: FirProjectSessionProvider) : FirModuleResolveState() {
|
override fun lazyResolveFunctionForCompletion(
|
||||||
internal open val cache: PsiToFirCache = PsiToFirCacheImpl()
|
firFunction: FirFunction<*>,
|
||||||
|
containerFirFile: FirFile,
|
||||||
private val diagnosticCache = mutableMapOf<KtElement, MutableList<Diagnostic>>()
|
firIdeProvider: FirIdeProvider,
|
||||||
|
toPhase: FirResolvePhase,
|
||||||
private val diagnosedFiles = mutableMapOf<KtFile, Long>()
|
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>
|
||||||
|
) {
|
||||||
override fun getCachedMapping(psi: KtElement): FirElement? = cache[psi]
|
elementBuilder.runLazyResolveForCompletion(firFunction, containerFirFile, firIdeProvider, toPhase, towerDataContextForStatement)
|
||||||
|
|
||||||
override fun getDiagnostics(psi: KtElement): List<Diagnostic> {
|
|
||||||
return diagnosticCache[psi] ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hasDiagnosticsForFile(file: KtFile): Boolean {
|
|
||||||
val previousStamp = diagnosedFiles[file] ?: return false
|
|
||||||
if (file.modificationStamp == previousStamp) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
diagnosedFiles.remove(file)
|
|
||||||
file.accept(object : PsiElementVisitor() {
|
|
||||||
override fun visitElement(element: PsiElement) {
|
|
||||||
cache.remove(element)
|
|
||||||
diagnosticCache.remove(element)
|
|
||||||
element.acceptChildren(this)
|
|
||||||
super.visitElement(element)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun record(psi: KtElement, fir: 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 record(psi: KtElement, diagnostic: Diagnostic) {
|
|
||||||
val list = diagnosticCache.getOrPut(psi) { mutableListOf() }
|
|
||||||
list += diagnostic
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setDiagnosticsForFile(file: KtFile, fir: FirFile, diagnostics: Iterable<FirDiagnostic<*>>) {
|
|
||||||
for (diagnostic in diagnostics) {
|
|
||||||
require(diagnostic is FirPsiDiagnostic<*>)
|
|
||||||
val psi = diagnostic.element.psi as? KtElement ?: continue
|
|
||||||
record(psi, diagnostic.asPsiBasedDiagnostic())
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnosedFiles[file] = file.modificationStamp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+50
-14
@@ -5,27 +5,63 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.fir.low.level.api
|
package org.jetbrains.kotlin.idea.fir.low.level.api
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
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.FirFunction
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.PsiToFirCache
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
class FirModuleResolveStateForCompletion(
|
internal class FirModuleResolveStateForCompletion(
|
||||||
mainState: FirModuleResolveStateImpl
|
private val originalState: FirModuleResolveStateImpl
|
||||||
) : FirModuleResolveStateImpl(mainState.sessionProvider) {
|
) : FirModuleResolveState() {
|
||||||
override val cache: PsiToFirCache = PsiToFirCacheForCompletion(mainState.cache)
|
override val moduleInfo: IdeaModuleInfo get() = originalState.moduleInfo
|
||||||
}
|
override val firSession: FirSession get() = originalState.firSession
|
||||||
|
|
||||||
private class PsiToFirCacheForCompletion(private val delegate: PsiToFirCache) : PsiToFirCache() {
|
private val psiToFirCache = PsiToFirCache(originalState.fileCache)
|
||||||
private val cache = mutableMapOf<KtElement, FirElement>()
|
|
||||||
|
|
||||||
override fun get(psi: KtElement): FirElement? =
|
override fun getSessionFor(moduleInfo: IdeaModuleInfo): FirSession =
|
||||||
cache[psi] ?: delegate[psi]
|
originalState.getSessionFor(moduleInfo)
|
||||||
|
|
||||||
override fun set(psi: KtElement, fir: FirElement) {
|
override fun getOrBuildFirFor(element: KtElement, toPhase: FirResolvePhase): FirElement {
|
||||||
cache[psi] = fir
|
getCachedMappingForCompletion(element)?.let { return it }
|
||||||
|
return originalState.elementBuilder.getOrBuildFirFor(
|
||||||
|
element,
|
||||||
|
originalState.fileCache,
|
||||||
|
psiToFirCache,
|
||||||
|
toPhase
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun remove(psi: PsiElement) {
|
override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
|
||||||
cache.remove(psi)
|
psiToFirCache.recordElementsForCompletionFrom(fir, firFile, ktFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCachedMappingForCompletion(element: KtElement): FirElement? {
|
||||||
|
psiToFirCache.getCachedMapping(element)?.let { return it }
|
||||||
|
originalState.psiToFirCache.getCachedMapping(element)?.let { return it }
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lazyResolveFunctionForCompletion(
|
||||||
|
firFunction: FirFunction<*>,
|
||||||
|
containerFirFile: FirFile,
|
||||||
|
firIdeProvider: FirIdeProvider,
|
||||||
|
toPhase: FirResolvePhase,
|
||||||
|
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>
|
||||||
|
) {
|
||||||
|
originalState.lazyResolveFunctionForCompletion(firFunction, containerFirFile, firIdeProvider, toPhase, towerDataContextForStatement)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDiagnostics(element: KtElement): List<Diagnostic> {
|
||||||
|
error("Diagnostics should not be retrieved in completion")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-326
@@ -1,326 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.idea.fir.low.level.api
|
|
||||||
|
|
||||||
import com.intellij.openapi.progress.ProgressIndicatorProvider
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import org.jetbrains.kotlin.fir.*
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
|
||||||
import org.jetbrains.kotlin.fir.psi
|
|
||||||
import org.jetbrains.kotlin.fir.references.*
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredCallableSymbols
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.createReturnTypeCalculatorForIDE
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.runResolve
|
|
||||||
import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
|
|
||||||
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
|
|
||||||
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
|
||||||
import org.jetbrains.kotlin.fir.visitors.compose
|
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
|
||||||
|
|
||||||
private val FirResolvePhase.stubMode: Boolean
|
|
||||||
get() = this <= FirResolvePhase.DECLARATIONS
|
|
||||||
|
|
||||||
private fun KtClassOrObject.relativeFqName(): FqName {
|
|
||||||
val className = this.nameAsSafeName
|
|
||||||
val parentFqName = this.containingClassOrObject?.relativeFqName()
|
|
||||||
return parentFqName?.child(className) ?: FqName.topLevel(className)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun FirFile.findCallableMember(
|
|
||||||
provider: FirProvider, callableMember: KtCallableDeclaration,
|
|
||||||
packageFqName: FqName, klassFqName: FqName?, declName: Name
|
|
||||||
): FirCallableDeclaration<*> {
|
|
||||||
if (klassFqName != null) {
|
|
||||||
return provider.getClassDeclaredCallableSymbols(ClassId(packageFqName, klassFqName, false), declName)
|
|
||||||
.find { symbol: FirCallableSymbol<*> ->
|
|
||||||
symbol.fir.psi == callableMember
|
|
||||||
}?.fir
|
|
||||||
?: error("Cannot find FIR callable declaration ${CallableId(packageFqName, klassFqName, declName)}")
|
|
||||||
}
|
|
||||||
// NB: not sure it's correct to use member scope provider from here (because of possible changes)
|
|
||||||
val memberScope = FirPackageMemberScope(this.packageFqName, session)
|
|
||||||
var result: FirCallableDeclaration<*>? = null
|
|
||||||
val processor = { symbol: FirCallableSymbol<*> ->
|
|
||||||
val fir = symbol.fir
|
|
||||||
if (result == null && fir.psi == callableMember) {
|
|
||||||
result = fir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (callableMember is KtNamedFunction || callableMember is KtConstructor<*>) {
|
|
||||||
memberScope.processFunctionsByName(declName, processor)
|
|
||||||
} else {
|
|
||||||
memberScope.processPropertiesByName(declName, processor)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
?: error("Cannot find FIR callable declaration ${CallableId(packageFqName, klassFqName, declName)}")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun KtCallableDeclaration.getOrBuildFir(
|
|
||||||
state: FirModuleResolveState,
|
|
||||||
phase: FirResolvePhase = FirResolvePhase.DECLARATIONS
|
|
||||||
): FirCallableDeclaration<*> {
|
|
||||||
val session = state.getSession(this)
|
|
||||||
|
|
||||||
val file = this.containingKtFile
|
|
||||||
val packageFqName = file.packageFqName
|
|
||||||
val klassFqName = this.containingClassOrObject?.relativeFqName()
|
|
||||||
val declName = this.nameAsSafeName
|
|
||||||
|
|
||||||
val firProvider = session.firIdeProvider
|
|
||||||
val firFile = firProvider.getOrBuildFile(file)
|
|
||||||
val firMemberSymbol = firFile.findCallableMember(firProvider, this, packageFqName, klassFqName, declName).symbol
|
|
||||||
val firMemberDeclaration = firMemberSymbol.fir
|
|
||||||
if (firMemberDeclaration.resolvePhase >= phase) {
|
|
||||||
return firMemberDeclaration
|
|
||||||
}
|
|
||||||
synchronized(firFile) {
|
|
||||||
firMemberDeclaration.runResolve(firFile, firProvider, phase, state)
|
|
||||||
}
|
|
||||||
return firMemberDeclaration
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun KtClassOrObject.getOrBuildFir(
|
|
||||||
state: FirModuleResolveState,
|
|
||||||
phase: FirResolvePhase = FirResolvePhase.DECLARATIONS
|
|
||||||
): FirMemberDeclaration {
|
|
||||||
val session = state.getSession(this)
|
|
||||||
|
|
||||||
val file = this.containingKtFile
|
|
||||||
val packageFqName = file.packageFqName
|
|
||||||
val klassFqName = this.relativeFqName()
|
|
||||||
|
|
||||||
val firProvider = session.firIdeProvider
|
|
||||||
val firFile = firProvider.getOrBuildFile(file)
|
|
||||||
|
|
||||||
val firClassOrEnumEntry = if (this is KtEnumEntry) {
|
|
||||||
val firEnumClass = firProvider.getFirClassifierByFqName(ClassId(packageFqName, klassFqName.parent(), false)) as FirRegularClass
|
|
||||||
firEnumClass.declarations.first { it is FirEnumEntry && it.name == this.nameAsSafeName } as FirMemberDeclaration
|
|
||||||
} else {
|
|
||||||
firProvider.getFirClassifierByFqName(ClassId(packageFqName, klassFqName, false)) as FirRegularClass
|
|
||||||
}
|
|
||||||
if (firClassOrEnumEntry.resolvePhase >= phase) {
|
|
||||||
return firClassOrEnumEntry
|
|
||||||
}
|
|
||||||
synchronized(firFile) {
|
|
||||||
firClassOrEnumEntry.runResolve(firFile, firProvider, phase, state)
|
|
||||||
}
|
|
||||||
return firClassOrEnumEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtFile.getOrBuildRawFirFile(state: FirModuleResolveState): Pair<FirIdeProvider, FirFile> {
|
|
||||||
val session = state.getSession(this)
|
|
||||||
val firProvider = session.firIdeProvider
|
|
||||||
return firProvider to firProvider.getOrBuildFile(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun KtFile.getOrBuildFir(
|
|
||||||
state: FirModuleResolveState,
|
|
||||||
phase: FirResolvePhase = FirResolvePhase.DECLARATIONS
|
|
||||||
): FirFile {
|
|
||||||
val (firProvider, firFile) = getOrBuildRawFirFile(state)
|
|
||||||
if (phase <= FirResolvePhase.DECLARATIONS && firFile.resolvePhase >= phase) {
|
|
||||||
return firFile
|
|
||||||
}
|
|
||||||
synchronized(firFile) {
|
|
||||||
firFile.runResolve(firFile, firProvider, phase, state)
|
|
||||||
}
|
|
||||||
return firFile
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun KtFile.getOrBuildFirWithDiagnostics(state: FirModuleResolveState): FirFile {
|
|
||||||
val (_, firFile) = getOrBuildRawFirFile(state)
|
|
||||||
val currentResolvePhase = firFile.resolvePhase
|
|
||||||
if (currentResolvePhase < FirResolvePhase.BODY_RESOLVE) {
|
|
||||||
synchronized(firFile) {
|
|
||||||
firFile.runResolve(toPhase = FirResolvePhase.BODY_RESOLVE, fromPhase = currentResolvePhase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressIndicatorProvider.checkCanceled() // ???
|
|
||||||
if (state.hasDiagnosticsForFile(this)) return firFile
|
|
||||||
|
|
||||||
FirIdeDiagnosticsCollector(firFile.session, state).collectDiagnostics(firFile)
|
|
||||||
state.setDiagnosticsForFile(this, firFile)
|
|
||||||
return firFile
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun FirDeclaration.runResolve(
|
|
||||||
file: FirFile,
|
|
||||||
firProvider: FirIdeProvider,
|
|
||||||
toPhase: FirResolvePhase,
|
|
||||||
state: FirModuleResolveState,
|
|
||||||
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>? = null,
|
|
||||||
) {
|
|
||||||
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
|
|
||||||
file.runResolve(toPhase = nonLazyPhase, fromPhase = this.resolvePhase)
|
|
||||||
if (toPhase <= nonLazyPhase) return
|
|
||||||
val designation = mutableListOf<FirDeclaration>(file)
|
|
||||||
if (this !is FirFile) {
|
|
||||||
val id = when (this) {
|
|
||||||
is FirCallableDeclaration<*> -> {
|
|
||||||
this.symbol.callableId.classId
|
|
||||||
}
|
|
||||||
is FirRegularClass -> {
|
|
||||||
this.symbol.classId
|
|
||||||
}
|
|
||||||
else -> error("Unsupported: ${render()}")
|
|
||||||
}
|
|
||||||
val outerClasses = generateSequence(id) { classId ->
|
|
||||||
classId.outerClassId
|
|
||||||
}.mapTo(mutableListOf()) { firProvider.getFirClassifierByFqName(it)!! }
|
|
||||||
designation += outerClasses.asReversed()
|
|
||||||
if (this is FirCallableDeclaration<*>) {
|
|
||||||
designation += this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (designation.all { it.resolvePhase >= toPhase }) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val scopeSession = ScopeSession()
|
|
||||||
val transformer = FirDesignatedBodyResolveTransformerForIDE(
|
|
||||||
designation.iterator(), state.getSession(psi as KtElement),
|
|
||||||
scopeSession,
|
|
||||||
implicitTypeOnly = toPhase == FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
|
|
||||||
towerDataContextForStatement
|
|
||||||
)
|
|
||||||
file.transform<FirFile, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FirDesignatedBodyResolveTransformerForIDE(
|
|
||||||
private val designation: Iterator<FirElement>,
|
|
||||||
session: FirSession,
|
|
||||||
scopeSession: ScopeSession,
|
|
||||||
implicitTypeOnly: Boolean,
|
|
||||||
private val towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>? = null
|
|
||||||
) : FirBodyResolveTransformer(
|
|
||||||
session,
|
|
||||||
phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
|
|
||||||
implicitTypeOnly = implicitTypeOnly,
|
|
||||||
scopeSession = scopeSession,
|
|
||||||
returnTypeCalculator = createReturnTypeCalculatorForIDE(session, scopeSession)
|
|
||||||
) {
|
|
||||||
|
|
||||||
override fun transformDeclarationContent(declaration: FirDeclaration, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
|
|
||||||
if (designation.hasNext()) {
|
|
||||||
designation.next().visitNoTransform(this, data)
|
|
||||||
return declaration.compose()
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.transformDeclarationContent(declaration, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBeforeStatementResolution(statement: FirStatement) {
|
|
||||||
if (towerDataContextForStatement == null) return
|
|
||||||
towerDataContextForStatement[statement] = context.towerDataContext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun KtElement.getNonLocalContainingDeclarationWithFqName(): KtDeclaration? {
|
|
||||||
var container = parent
|
|
||||||
while (container != null && container !is KtFile) {
|
|
||||||
if (container is KtDeclaration
|
|
||||||
&& (container is KtClassOrObject || container is KtDeclarationWithBody)
|
|
||||||
&& !KtPsiUtil.isLocal(container)
|
|
||||||
&& container.name != null
|
|
||||||
&& container !is KtEnumEntry
|
|
||||||
&& container.containingClassOrObject !is KtEnumEntry
|
|
||||||
) {
|
|
||||||
return container
|
|
||||||
}
|
|
||||||
container = container.parent
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun KtElement.getOrBuildFir(
|
|
||||||
state: FirModuleResolveState,
|
|
||||||
phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE
|
|
||||||
): FirElement {
|
|
||||||
val containerFir: FirDeclaration =
|
|
||||||
when (val container = getNonLocalContainingDeclarationWithFqName()) {
|
|
||||||
is KtCallableDeclaration -> container.getOrBuildFir(state, phase)
|
|
||||||
is KtClassOrObject -> container.getOrBuildFir(state, phase)
|
|
||||||
null -> containingKtFile.getOrBuildFir(state, phase)
|
|
||||||
else -> error("Unsupported: ${container.text}")
|
|
||||||
}
|
|
||||||
|
|
||||||
val psi = when (this) {
|
|
||||||
is KtPropertyDelegate -> this.expression ?: this
|
|
||||||
else -> this
|
|
||||||
}
|
|
||||||
return state.getCachedMapping(this) ?: run {
|
|
||||||
state.recordElementsFrom(containerFir)
|
|
||||||
|
|
||||||
val (current, mappedFir) = psi.getFirOfClosestParent(state) ?: error("FirElement is not found for: $text")
|
|
||||||
if (current != this) {
|
|
||||||
state.record(current, mappedFir)
|
|
||||||
}
|
|
||||||
|
|
||||||
mappedFir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun KtElement.getFirOfClosestParent(state: FirModuleResolveState): Pair<KtElement, FirElement>? {
|
|
||||||
var current: PsiElement? = this
|
|
||||||
while (current is KtElement) {
|
|
||||||
val mappedFir = state.getCachedMapping(current)
|
|
||||||
if (mappedFir != null) {
|
|
||||||
return current to mappedFir
|
|
||||||
}
|
|
||||||
current = current.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun FirModuleResolveState.recordElementsFrom(containerFir: FirDeclaration) {
|
|
||||||
containerFir.accept(object : FirVisitorVoid() {
|
|
||||||
override fun visitElement(element: FirElement) {
|
|
||||||
(element.realPsi as? KtElement)?.let {
|
|
||||||
record(it, element)
|
|
||||||
}
|
|
||||||
element.acceptChildren(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitReference(reference: FirReference) {}
|
|
||||||
|
|
||||||
override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference) {}
|
|
||||||
|
|
||||||
override fun visitNamedReference(namedReference: FirNamedReference) {}
|
|
||||||
|
|
||||||
override fun visitResolvedNamedReference(resolvedNamedReference: FirResolvedNamedReference) {}
|
|
||||||
|
|
||||||
override fun visitDelegateFieldReference(delegateFieldReference: FirDelegateFieldReference) {}
|
|
||||||
|
|
||||||
override fun visitBackingFieldReference(backingFieldReference: FirBackingFieldReference) {}
|
|
||||||
|
|
||||||
override fun visitSuperReference(superReference: FirSuperReference) {}
|
|
||||||
|
|
||||||
override fun visitThisReference(thisReference: FirThisReference) {}
|
|
||||||
|
|
||||||
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef) {}
|
|
||||||
|
|
||||||
override fun visitUserTypeRef(userTypeRef: FirUserTypeRef) {
|
|
||||||
userTypeRef.acceptChildren(this)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.transformers.PhasedFirFileResolver
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
|
|
||||||
|
internal class IdePhasedFirFileResolver(
|
||||||
|
private val firFileBuilder: FirFileBuilder,
|
||||||
|
private val cache: ModuleFileCache
|
||||||
|
) : PhasedFirFileResolver() {
|
||||||
|
override fun resolveFile(firFile: FirFile, fromPhase: FirResolvePhase, toPhase: FirResolvePhase) {
|
||||||
|
firFileBuilder.runResolve(firFile, cache, fromPhase, toPhase)
|
||||||
|
}
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.psi.stubs.StubIndex
|
||||||
|
import com.intellij.psi.stubs.StubIndexKey
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.IndexHelper.Companion.asStringForIndexes
|
||||||
|
import org.jetbrains.kotlin.idea.stubindex.*
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
|
|
||||||
|
internal class IndexHelper(val project: Project, private val scope: GlobalSearchScope) {
|
||||||
|
private val stubIndex: StubIndex = StubIndex.getInstance()
|
||||||
|
|
||||||
|
private inline fun <INDEX_KEY : Any, reified PSI : PsiElement> firstMatchingOrNull(
|
||||||
|
stubKey: StubIndexKey<INDEX_KEY, PSI>,
|
||||||
|
key: INDEX_KEY,
|
||||||
|
crossinline filter: (PSI) -> Boolean
|
||||||
|
): PSI? {
|
||||||
|
var result: PSI? = null
|
||||||
|
stubIndex.processElements(
|
||||||
|
stubKey, key, project, scope, PSI::class.java,
|
||||||
|
) { candidate ->
|
||||||
|
if (filter(candidate)) {
|
||||||
|
result = candidate
|
||||||
|
return@processElements false // do not continue searching over PSI
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun classFromIndexByClassId(classId: ClassId) = firstMatchingOrNull(
|
||||||
|
KotlinFullClassNameIndex.KEY,
|
||||||
|
classId.asStringForIndexes(),
|
||||||
|
) { candidate -> candidate.containingKtFile.packageFqName == classId.packageFqName }
|
||||||
|
|
||||||
|
fun typeAliasFromIndexByClassId(classId: ClassId) = firstMatchingOrNull(
|
||||||
|
KotlinTopLevelTypeAliasFqNameIndex.KEY,
|
||||||
|
classId.asStringForIndexes(),
|
||||||
|
) { candidate -> candidate.containingKtFile.packageFqName == classId.packageFqName }
|
||||||
|
|
||||||
|
|
||||||
|
fun getTopLevelProperties(callableId: CallableId): Collection<KtProperty> =
|
||||||
|
KotlinTopLevelPropertyFqnNameIndex.getInstance()[callableId.asStringForIndexes(), project, scope]
|
||||||
|
|
||||||
|
fun getTopLevelFunctions(callableId: CallableId): Collection<KtNamedFunction> =
|
||||||
|
KotlinTopLevelFunctionFqnNameIndex.getInstance()[callableId.asStringForIndexes(), project, scope]
|
||||||
|
|
||||||
|
fun getTopLevelPropertiesInPackage(packageFqName: FqName): Collection<KtProperty> =
|
||||||
|
KotlinTopLevelPropertyByPackageIndex.getInstance().get(packageFqName.asStringForIndexes(), project, scope)
|
||||||
|
|
||||||
|
fun getTopLevelFunctionsInPackage(packageFqName: FqName): Collection<KtFunction> =
|
||||||
|
KotlinTopLevelFunctionByPackageIndex.getInstance().get(packageFqName.asStringForIndexes(), project, scope)
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun CallableId.asStringForIndexes(): String =
|
||||||
|
(if (packageName.isRoot) callableName.asString() else toString()).replace('/', '.')
|
||||||
|
|
||||||
|
private fun FqName.asStringForIndexes(): String =
|
||||||
|
asString().replace('/', '.')
|
||||||
|
|
||||||
|
private fun ClassId.asStringForIndexes(): String =
|
||||||
|
asSingleFqName().asStringForIndexes()
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
-17
@@ -12,27 +12,28 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
|
|||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
import org.jetbrains.kotlin.fir.psi
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
import org.jetbrains.kotlin.fir.psi
|
||||||
|
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
||||||
|
|
||||||
object LowLevelFirApiFacade {
|
object LowLevelFirApiFacade {
|
||||||
fun getResolveStateFor(element: KtElement): FirModuleResolveState =
|
fun getResolveStateFor(element: KtElement): FirModuleResolveState =
|
||||||
element.firResolveState()
|
element.firResolveState()
|
||||||
|
|
||||||
fun getResolveStateForCompletion(element: KtElement, mainState: FirModuleResolveStateImpl): FirModuleResolveStateForCompletion {
|
fun getResolveStateForCompletion(element: KtElement, originalState: FirModuleResolveState): FirModuleResolveState {
|
||||||
return FirModuleResolveStateForCompletion(mainState)
|
check(originalState is FirModuleResolveStateImpl)
|
||||||
|
return FirModuleResolveStateForCompletion(originalState)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSessionFor(element: KtElement, resolveState: FirModuleResolveState): FirSession =
|
fun getSessionFor(element: KtElement): FirSession =
|
||||||
resolveState.getSession(element)
|
getResolveStateFor(element).getSessionFor(element.getModuleInfo())
|
||||||
|
|
||||||
fun getOrBuildFirFor(element: KtElement, resolveState: FirModuleResolveState, phase: FirResolvePhase): FirElement =
|
fun getOrBuildFirFor(element: KtElement, resolveState: FirModuleResolveState, phase: FirResolvePhase): FirElement =
|
||||||
element.getOrBuildFir(resolveState, phase)
|
resolveState.getOrBuildFirFor(element, phase)
|
||||||
|
|
||||||
|
|
||||||
fun getFirOfClosestParent(element: KtElement): FirElement? = element.getFirOfClosestParent(element.firResolveState())?.second
|
|
||||||
|
|
||||||
class FirCompletionContext internal constructor(
|
class FirCompletionContext internal constructor(
|
||||||
val session: FirSession,
|
val session: FirSession,
|
||||||
@@ -42,7 +43,7 @@ object LowLevelFirApiFacade {
|
|||||||
fun getTowerDataContext(element: KtElement): FirTowerDataContext {
|
fun getTowerDataContext(element: KtElement): FirTowerDataContext {
|
||||||
var current: PsiElement? = element
|
var current: PsiElement? = element
|
||||||
while (current is KtElement) {
|
while (current is KtElement) {
|
||||||
val mappedFir = state.getCachedMapping(current)
|
val mappedFir = state.getCachedMappingForCompletion(current)
|
||||||
|
|
||||||
if (mappedFir is FirStatement) {
|
if (mappedFir is FirStatement) {
|
||||||
towerDataContextForStatement[mappedFir]?.let { return it }
|
towerDataContextForStatement[mappedFir]?.let { return it }
|
||||||
@@ -56,7 +57,7 @@ object LowLevelFirApiFacade {
|
|||||||
current = current.parent
|
current = current.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
error("No context for $element")
|
error("No context for ${element.getElementTextInContext()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,10 +72,8 @@ object LowLevelFirApiFacade {
|
|||||||
val towerDataContextForStatement = mutableMapOf<FirStatement, FirTowerDataContext>()
|
val towerDataContextForStatement = mutableMapOf<FirStatement, FirTowerDataContext>()
|
||||||
|
|
||||||
val function = builtFunction.apply {
|
val function = builtFunction.apply {
|
||||||
runResolve(firFile, firIdeProvider, phase, state, towerDataContextForStatement)
|
state.lazyResolveFunctionForCompletion(this, firFile, firIdeProvider, phase, towerDataContextForStatement)
|
||||||
|
state.recordPsiToFirMappingsForCompletionFrom(this, firFile, element.containingKtFile)
|
||||||
// TODO this PSI caching should be somewhere else
|
|
||||||
state.recordElementsFrom(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FirCompletionContext(
|
return FirCompletionContext(
|
||||||
@@ -85,8 +84,6 @@ object LowLevelFirApiFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getDiagnosticsFor(element: KtElement, resolveState: FirModuleResolveState): Collection<Diagnostic> {
|
fun getDiagnosticsFor(element: KtElement, resolveState: FirModuleResolveState): Collection<Diagnostic> {
|
||||||
val file = element.containingKtFile
|
|
||||||
file.getOrBuildFirWithDiagnostics(resolveState)
|
|
||||||
return resolveState.getDiagnostics(element)
|
return resolveState.getDiagnostics(element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
|
import org.jetbrains.kotlin.analyzer.PackageOracleFactory
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
|
internal sealed class PackageExistenceChecker {
|
||||||
|
abstract fun isPackageExists(packageFqName: FqName): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PackageExistenceCheckerForSingleModule(
|
||||||
|
project: Project,
|
||||||
|
module: ModuleInfo
|
||||||
|
) : PackageExistenceChecker() {
|
||||||
|
private val oracle =
|
||||||
|
project.service<IdePackageOracleFactory>().createOracle(module)
|
||||||
|
override fun isPackageExists(packageFqName: FqName): Boolean = oracle.packageExists(packageFqName)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PackageExistenceCheckerForMultipleModules(
|
||||||
|
project: Project,
|
||||||
|
modules: List<ModuleInfo>
|
||||||
|
) : PackageExistenceChecker() {
|
||||||
|
private val oracles = run {
|
||||||
|
val factory = project.service<IdePackageOracleFactory>()
|
||||||
|
modules.map { factory.createOracle(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isPackageExists(packageFqName: FqName): Boolean =
|
||||||
|
oracles.any { oracle -> oracle.packageExists(packageFqName) }
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.diagnostics
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
internal class DiagnosticsCollector(
|
||||||
|
private val firFileBuilder: FirFileBuilder,
|
||||||
|
private val cache: ModuleFileCache,
|
||||||
|
) {
|
||||||
|
private val diagnosticsForFile = ConcurrentHashMap<KtFile, DiagnosticsForFile>()
|
||||||
|
|
||||||
|
fun getDiagnosticsFor(element: KtElement): List<Diagnostic> {
|
||||||
|
val ktFile = element.containingKtFile
|
||||||
|
val diagnostics = diagnosticsForFile.computeIfAbsent(ktFile) {
|
||||||
|
val firFile = firFileBuilder.getFirFileResolvedToPhaseWithCaching(ktFile, cache, toPhase = FirResolvePhase.BODY_RESOLVE)
|
||||||
|
DiagnosticsForFile.collectDiagnosticsForFile(firFile)
|
||||||
|
}
|
||||||
|
return diagnostics.getDiagnosticsFor(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DiagnosticsForFile private constructor(private val diagnostics: Map<KtElement, List<Diagnostic>>) {
|
||||||
|
fun getDiagnosticsFor(element: KtElement): List<Diagnostic> = diagnostics[element].orEmpty()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Collects diagnostics for given [firFile]
|
||||||
|
* Should be called under [firFile]-based lock
|
||||||
|
*/
|
||||||
|
fun collectDiagnosticsForFile(firFile: FirFile): DiagnosticsForFile {
|
||||||
|
require(firFile.resolvePhase >= FirResolvePhase.BODY_RESOLVE) {
|
||||||
|
"To collect diagnostics at least FirResolvePhase.BODY_RESOLVE is needed, but file ${firFile.name} was resolved to ${firFile.resolvePhase}"
|
||||||
|
}
|
||||||
|
return DiagnosticsForFile(FirIdeDiagnosticsCollector.collect(firFile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
-3
@@ -3,17 +3,23 @@
|
|||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* 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.idea.fir.low.level.api
|
package org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
|
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
|
||||||
import org.jetbrains.kotlin.fir.analysis.collectors.registerAllComponents
|
import org.jetbrains.kotlin.fir.analysis.collectors.registerAllComponents
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.addValueFor
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
|
||||||
internal class FirIdeDiagnosticsCollector(session: FirSession, private val resolveState: FirModuleResolveState) : AbstractDiagnosticCollector(session) {
|
internal class FirIdeDiagnosticsCollector private constructor(
|
||||||
|
session: FirSession,
|
||||||
|
) : AbstractDiagnosticCollector(session) {
|
||||||
|
private val result = mutableMapOf<KtElement, MutableList<Diagnostic>>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
registerAllComponents()
|
registerAllComponents()
|
||||||
@@ -23,7 +29,7 @@ internal class FirIdeDiagnosticsCollector(session: FirSession, private val resol
|
|||||||
override fun report(diagnostic: FirDiagnostic<*>?) {
|
override fun report(diagnostic: FirDiagnostic<*>?) {
|
||||||
if (diagnostic !is FirPsiDiagnostic<*>) return
|
if (diagnostic !is FirPsiDiagnostic<*>) return
|
||||||
val psi = diagnostic.element.psi as? KtElement ?: return
|
val psi = diagnostic.element.psi as? KtElement ?: return
|
||||||
resolveState.record(psi, diagnostic.asPsiBasedDiagnostic())
|
result.addValueFor(psi, diagnostic.asPsiBasedDiagnostic())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,4 +47,16 @@ internal class FirIdeDiagnosticsCollector(session: FirSession, private val resol
|
|||||||
override fun runCheck(block: (DiagnosticReporter) -> Unit) {
|
override fun runCheck(block: (DiagnosticReporter) -> Unit) {
|
||||||
block(reporter)
|
block(reporter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Collects diagnostics for given [firFile]
|
||||||
|
* Should be called under [firFile]-based lock
|
||||||
|
*/
|
||||||
|
fun collect(firFile: FirFile): Map<KtElement, List<Diagnostic>> =
|
||||||
|
FirIdeDiagnosticsCollector(firFile.session).let { collector ->
|
||||||
|
collector.collectDiagnostics(firFile)
|
||||||
|
collector.result
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+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.idea.fir.low.level.api.element.builder
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.render
|
||||||
|
import org.jetbrains.kotlin.idea.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
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.element.builder
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
||||||
|
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.createReturnTypeCalculatorForIDE
|
||||||
|
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
||||||
|
import org.jetbrains.kotlin.fir.visitors.compose
|
||||||
|
|
||||||
|
internal class FirDesignatedBodyResolveTransformerForIDE(
|
||||||
|
private val designation: Iterator<FirElement>,
|
||||||
|
session: FirSession,
|
||||||
|
scopeSession: ScopeSession,
|
||||||
|
implicitTypeOnly: Boolean,
|
||||||
|
private val towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>? = null
|
||||||
|
) : FirBodyResolveTransformer(
|
||||||
|
session,
|
||||||
|
phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
|
||||||
|
implicitTypeOnly = implicitTypeOnly,
|
||||||
|
scopeSession = scopeSession,
|
||||||
|
returnTypeCalculator = createReturnTypeCalculatorForIDE(session, scopeSession)
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun transformDeclarationContent(declaration: FirDeclaration, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
|
||||||
|
if (designation.hasNext()) {
|
||||||
|
designation.next().visitNoTransform(this, data)
|
||||||
|
return declaration.compose()
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.transformDeclarationContent(declaration, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBeforeStatementResolution(statement: FirStatement) {
|
||||||
|
if (towerDataContextForStatement == null) return
|
||||||
|
towerDataContextForStatement[statement] = context.towerDataContext
|
||||||
|
}
|
||||||
|
}
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.element.builder
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
|
import org.jetbrains.kotlin.fir.render
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.FirElementFinder
|
||||||
|
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps [KtElement] to [FirElement]
|
||||||
|
* Stateless, caches everything into [ModuleFileCache] & [PsiToFirCache] passed into the function
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
internal class FirElementBuilder(
|
||||||
|
private val firFileBuilder: FirFileBuilder,
|
||||||
|
) {
|
||||||
|
fun getOrBuildFirFor(
|
||||||
|
element: KtElement,
|
||||||
|
moduleFileCache: ModuleFileCache,
|
||||||
|
psiToFirCache: PsiToFirCache,
|
||||||
|
toPhase: FirResolvePhase,
|
||||||
|
): FirElement {
|
||||||
|
val ktFile = element.containingKtFile
|
||||||
|
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache)
|
||||||
|
|
||||||
|
val containerFir = when (val container = element.getNonLocalContainingDeclarationWithFqName()) {
|
||||||
|
is KtDeclaration -> {
|
||||||
|
FirElementFinder.findElementByPsiIn<FirDeclaration>(firFile, container)
|
||||||
|
?: error("Declaration was not found in FIR file which was build by declaration KtFile, declaration is\n${container.getElementTextInContext()}")
|
||||||
|
}
|
||||||
|
null -> firFile
|
||||||
|
else -> error("Unsupported: ${container.text}")
|
||||||
|
}
|
||||||
|
|
||||||
|
firFileBuilder.runCustomResolve(firFile, moduleFileCache) {
|
||||||
|
runLazyResolveWithoutLock(containerFir, firFile, firFile.session.firIdeProvider, toPhase)
|
||||||
|
}
|
||||||
|
|
||||||
|
return psiToFirCache.getFir(element, containerFir, firFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be invoked under lock
|
||||||
|
*/
|
||||||
|
private fun runLazyResolveWithoutLock(
|
||||||
|
firDeclarationToResolve: FirDeclaration,
|
||||||
|
containerFirFile: FirFile,
|
||||||
|
firIdeProvider: FirIdeProvider,
|
||||||
|
toPhase: FirResolvePhase,
|
||||||
|
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>? = null,
|
||||||
|
) {
|
||||||
|
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
|
||||||
|
if (firDeclarationToResolve.resolvePhase < nonLazyPhase) {
|
||||||
|
firFileBuilder.runResolveWithoutLock(containerFirFile, fromPhase = firDeclarationToResolve.resolvePhase, toPhase = nonLazyPhase)
|
||||||
|
}
|
||||||
|
if (toPhase <= nonLazyPhase) return
|
||||||
|
val designation = mutableListOf<FirDeclaration>(containerFirFile)
|
||||||
|
if (firDeclarationToResolve !is FirFile) {
|
||||||
|
val id = when (firDeclarationToResolve) {
|
||||||
|
is FirCallableDeclaration<*> -> {
|
||||||
|
firDeclarationToResolve.symbol.callableId.classId
|
||||||
|
}
|
||||||
|
is FirRegularClass -> {
|
||||||
|
firDeclarationToResolve.symbol.classId
|
||||||
|
}
|
||||||
|
else -> error("Unsupported: ${firDeclarationToResolve.render()}")
|
||||||
|
}
|
||||||
|
val outerClasses = generateSequence(id) { classId ->
|
||||||
|
classId.outerClassId
|
||||||
|
}.mapTo(mutableListOf()) { firIdeProvider.getFirClassifierByFqName(it)!! }
|
||||||
|
designation += outerClasses.asReversed()
|
||||||
|
if (firDeclarationToResolve is FirCallableDeclaration<*>) {
|
||||||
|
designation += firDeclarationToResolve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (designation.all { it.resolvePhase >= toPhase }) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val scopeSession = ScopeSession()
|
||||||
|
val transformer = FirDesignatedBodyResolveTransformerForIDE(
|
||||||
|
designation.iterator(), containerFirFile.session,
|
||||||
|
scopeSession,
|
||||||
|
implicitTypeOnly = toPhase == FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
|
||||||
|
towerDataContextForStatement
|
||||||
|
)
|
||||||
|
containerFirFile.transform<FirFile, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO for completion only
|
||||||
|
fun runLazyResolveForCompletion(
|
||||||
|
firFunction: FirFunction<*>,
|
||||||
|
containerFirFile: FirFile,
|
||||||
|
firIdeProvider: FirIdeProvider,
|
||||||
|
toPhase: FirResolvePhase,
|
||||||
|
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>
|
||||||
|
) {
|
||||||
|
runLazyResolveWithoutLock(firFunction, containerFirFile, firIdeProvider, toPhase, towerDataContextForStatement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun KtElement.getNonLocalContainingDeclarationWithFqName(): KtDeclaration? {
|
||||||
|
var container = parent
|
||||||
|
while (container != null && container !is KtFile) {
|
||||||
|
if (container is KtDeclaration
|
||||||
|
&& (container is KtClassOrObject || container is KtDeclarationWithBody)
|
||||||
|
&& !KtPsiUtil.isLocal(container)
|
||||||
|
&& container.name != null
|
||||||
|
&& container !is KtEnumEntry
|
||||||
|
&& container.containingClassOrObject !is KtEnumEntry
|
||||||
|
) {
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
container = container.parent
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.element.builder
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
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.realPsi
|
||||||
|
import org.jetbrains.kotlin.fir.references.*
|
||||||
|
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
|
||||||
|
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.FirVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.isErrorElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import org.jetbrains.kotlin.psi.KtPropertyDelegate
|
||||||
|
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Belongs to a [org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveState]
|
||||||
|
*/
|
||||||
|
internal class PsiToFirCache(private val moduleFileCache: ModuleFileCache) {
|
||||||
|
private val caches = ConcurrentHashMap<KtFile, FileCache>()
|
||||||
|
|
||||||
|
fun getCachedMapping(element: KtElement): FirElement? {
|
||||||
|
val ktFile = element.containingKtFile
|
||||||
|
val cache = caches[ktFile]
|
||||||
|
return cache?.getCachedMapping(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFir(element: KtElement, containerFir: FirDeclaration, firFile: FirFile): FirElement {
|
||||||
|
val ktFile = element.containingKtFile
|
||||||
|
val cache = caches.getOrPut(ktFile) { FileCache(ktFile, firFile, moduleFileCache) }
|
||||||
|
return cache.getFir(element, containerFir)
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo for completion only
|
||||||
|
fun recordElementsForCompletionFrom(containerFir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
|
||||||
|
val cache = caches.getOrPut(ktFile) { FileCache(ktFile, firFile, moduleFileCache) }
|
||||||
|
cache.recordElementsForCompletionFrom(containerFir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class FileCache(val ktFile: KtFile, firFile: FirFile, moduleFileCache: ModuleFileCache) {
|
||||||
|
private val cache: ConcurrentHashMap<KtElement, FirElement> = ConcurrentHashMap()
|
||||||
|
|
||||||
|
fun getCachedMapping(ktElement: KtElement): FirElement? {
|
||||||
|
require(ktElement.containingKtFile === ktFile)
|
||||||
|
return cache[ktElement]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFir(element: KtElement, containerFir: FirDeclaration): FirElement {
|
||||||
|
val psi = when (element) {
|
||||||
|
is KtPropertyDelegate -> element.expression ?: element
|
||||||
|
else -> element
|
||||||
|
}
|
||||||
|
cache[psi]?.let { return it }
|
||||||
|
recordElementsFrom(containerFir)
|
||||||
|
|
||||||
|
val (current, mappedFir) = psi.getFirOfClosestParent()
|
||||||
|
?: error("FirElement is not found for:\n${element.getElementTextInContext()}")
|
||||||
|
if (current !== element) {
|
||||||
|
cache(current, mappedFir)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedFir
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cache(psi: KtElement, fir: FirElement) {
|
||||||
|
// todo make it thread safe
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun recordElementsForCompletionFrom(firDeclaration: FirDeclaration) {
|
||||||
|
recordElementsFrom(firDeclaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recordElementsFrom(firDeclaration: FirDeclaration) {
|
||||||
|
firDeclaration.accept(elementsRecorder)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val elementsRecorder = object : FirVisitorVoid() {
|
||||||
|
override fun visitElement(element: FirElement) {
|
||||||
|
(element.realPsi as? KtElement)?.let { psi ->
|
||||||
|
cache(psi, element)
|
||||||
|
}
|
||||||
|
element.acceptChildren(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitReference(reference: FirReference) {}
|
||||||
|
override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference) {}
|
||||||
|
override fun visitNamedReference(namedReference: FirNamedReference) {}
|
||||||
|
override fun visitResolvedNamedReference(resolvedNamedReference: FirResolvedNamedReference) {}
|
||||||
|
override fun visitDelegateFieldReference(delegateFieldReference: FirDelegateFieldReference) {}
|
||||||
|
override fun visitBackingFieldReference(backingFieldReference: FirBackingFieldReference) {}
|
||||||
|
override fun visitSuperReference(superReference: FirSuperReference) {}
|
||||||
|
override fun visitThisReference(thisReference: FirThisReference) {}
|
||||||
|
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef) {}
|
||||||
|
|
||||||
|
override fun visitUserTypeRef(userTypeRef: FirUserTypeRef) {
|
||||||
|
userTypeRef.acceptChildren(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtElement.getFirOfClosestParent(): Pair<KtElement, FirElement>? {
|
||||||
|
var current: PsiElement? = this
|
||||||
|
while (current is KtElement) {
|
||||||
|
val mappedFir = cache[current]
|
||||||
|
if (mappedFir != null) {
|
||||||
|
return current to mappedFir
|
||||||
|
}
|
||||||
|
current = current.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.file.builder
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
|
||||||
|
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.fir.resolve.transformers.createTransformerBasedProcessorByPhase
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.FirIdeSessionProvider
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for building [FirFile] by [KtFile]
|
||||||
|
* Stateless, all caches are stored in [ModuleFileCache] passed into corresponding functions
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
internal class FirFileBuilder(
|
||||||
|
private val sessionProvider: FirIdeSessionProvider,
|
||||||
|
private val scopeProvider: FirScopeProvider
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
): FirFile = cache.fileCached(ktFile) {
|
||||||
|
RawFirBuilder(cache.session, scopeProvider, stubMode = false).buildFirFile(ktFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFirFileResolvedToPhaseWithCaching(
|
||||||
|
ktFile: KtFile,
|
||||||
|
cache: ModuleFileCache,
|
||||||
|
@Suppress("SameParameterValue") toPhase: FirResolvePhase
|
||||||
|
): FirFile {
|
||||||
|
val firFile = buildRawFirFileWithCaching(ktFile, cache)
|
||||||
|
if (toPhase > FirResolvePhase.RAW_FIR) {
|
||||||
|
cache.firFileLockProvider.withLock(firFile) {
|
||||||
|
//add lock for implit type resolve phase & super type
|
||||||
|
runResolveWithoutLock(firFile, fromPhase = firFile.resolvePhase, toPhase = toPhase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firFile
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs [resolve] function (which is considered to do some resolve on [firFile]) under a lock for [firFile]
|
||||||
|
*/
|
||||||
|
inline fun <R> runCustomResolve(firFile: FirFile, cache: ModuleFileCache, resolve: () -> R): R =
|
||||||
|
cache.firFileLockProvider.withLock(firFile) { resolve() }
|
||||||
|
|
||||||
|
fun runResolve(firFile: FirFile, cache: ModuleFileCache, fromPhase: FirResolvePhase, toPhase: FirResolvePhase) {
|
||||||
|
cache.firFileLockProvider.withLock(firFile) {
|
||||||
|
runResolveWithoutLock(firFile, fromPhase, toPhase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runResolveWithoutLock(firFile: FirFile, fromPhase: FirResolvePhase, toPhase: FirResolvePhase) {
|
||||||
|
assert(fromPhase <= toPhase) {
|
||||||
|
"Trying to resolve file ${firFile.name} from $fromPhase to $toPhase"
|
||||||
|
}
|
||||||
|
val scopeSession = ScopeSession()
|
||||||
|
var currentPhase = fromPhase
|
||||||
|
while (currentPhase < toPhase) {
|
||||||
|
currentPhase = currentPhase.next
|
||||||
|
val phaseProcessor = currentPhase.createTransformerBasedProcessorByPhase(firFile.session, scopeSession)
|
||||||
|
phaseProcessor.processFile(firFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.file.builder
|
||||||
|
|
||||||
|
import com.google.common.collect.MapMaker
|
||||||
|
import java.util.concurrent.ConcurrentMap
|
||||||
|
import java.util.concurrent.locks.ReadWriteLock
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
internal class LockProvider<KEY, out LOCK>(private val createLock: () -> LOCK) {
|
||||||
|
private val locks: ConcurrentMap<KEY, LOCK> = MapMaker().weakKeys().makeMap()
|
||||||
|
fun getLockFor(key: KEY) = locks.getOrPut(key) { createLock() }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun <KEY, R> LockProvider<KEY, ReadWriteLock>.withReadLock(key: KEY, action: () -> R): R {
|
||||||
|
val readLock = getLockFor(key).readLock()
|
||||||
|
return readLock.withLock { action() }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun <KEY, R> LockProvider<KEY, ReadWriteLock>.withWriteLock(key: KEY, action: () -> R): R {
|
||||||
|
val writeLock = getLockFor(key).writeLock()
|
||||||
|
return writeLock.withLock { action() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal inline fun <KEY, R> LockProvider<KEY, ReentrantLock>.withLock(key: KEY, action: () -> R): R {
|
||||||
|
val lock = getLockFor(key)
|
||||||
|
return lock.withLock { action() }
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.file.builder
|
||||||
|
|
||||||
|
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.symbols.CallableId
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 getCachedFirFile(ktFile: KtFile): FirFile?
|
||||||
|
|
||||||
|
// todo make it ReadWriteLock and allow access fir elements only under read lock
|
||||||
|
// for now locks only held for resolve
|
||||||
|
// but there can be a situation when we are accessing some fir element in one thread without lock
|
||||||
|
// in the same time other thread performs resolve of it
|
||||||
|
// which can cause weird errors on user side
|
||||||
|
abstract val firFileLockProvider: LockProvider<FirFile, ReentrantLock>
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ModuleFileCacheImpl(override val session: FirSession) : ModuleFileCache() {
|
||||||
|
private val ktFileToFirFile = ConcurrentHashMap<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 val firFileLockProvider: LockProvider<FirFile, ReentrantLock> = LockProvider { ReentrantLock() }
|
||||||
|
}
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.providers
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.providers.FirProviderInternals
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirAccessorSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.IndexHelper
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.PackageExistenceCheckerForMultipleModules
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.collectTransitiveDependenciesWithSelf
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
|
||||||
|
internal class FirIdeProvider(
|
||||||
|
project: Project,
|
||||||
|
val session: FirSession,
|
||||||
|
moduleInfo: ModuleSourceInfo,
|
||||||
|
private val kotlinScopeProvider: KotlinScopeProvider,
|
||||||
|
firFileBuilder: FirFileBuilder,
|
||||||
|
val cache: ModuleFileCache,
|
||||||
|
searchScope: GlobalSearchScope,
|
||||||
|
) : FirProvider() {
|
||||||
|
private val indexHelper = IndexHelper(project, searchScope)
|
||||||
|
private val packageExistenceChecker = PackageExistenceCheckerForMultipleModules(
|
||||||
|
project,
|
||||||
|
moduleInfo.collectTransitiveDependenciesWithSelf().filterIsInstance<ModuleSourceInfo>()
|
||||||
|
)
|
||||||
|
|
||||||
|
private val providerHelper = FirProviderHelper(
|
||||||
|
cache,
|
||||||
|
firFileBuilder,
|
||||||
|
indexHelper,
|
||||||
|
packageExistenceChecker,
|
||||||
|
)
|
||||||
|
|
||||||
|
override val isPhasedFirAllowed: Boolean get() = true
|
||||||
|
|
||||||
|
override fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration<*>? =
|
||||||
|
providerHelper.getFirClassifierByFqName(classId)
|
||||||
|
|
||||||
|
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> =
|
||||||
|
providerHelper.getTopLevelCallableSymbols(packageFqName, name)
|
||||||
|
|
||||||
|
override fun getPackage(fqName: FqName): FqName? =
|
||||||
|
providerHelper.getPackage(fqName)
|
||||||
|
|
||||||
|
override fun getNestedClassifierScope(classId: ClassId): FirScope? =
|
||||||
|
providerHelper.getNestedClassifierScope(classId)
|
||||||
|
|
||||||
|
override fun getClassLikeSymbolByFqName(classId: ClassId): FirClassLikeSymbol<*>? {
|
||||||
|
return getFirClassifierByFqName(classId)?.symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
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 providerHelper.getContainingFirFile(fir.symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFirClassifierContainerFile(symbol: FirClassLikeSymbol<*>): FirFile {
|
||||||
|
return getFirClassifierContainerFileIfAny(symbol)
|
||||||
|
?: error("Couldn't find container for ${symbol.classId}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFirClassifierContainerFileIfAny(symbol: FirClassLikeSymbol<*>): FirFile? =
|
||||||
|
providerHelper.getContainingFirFile(symbol)
|
||||||
|
|
||||||
|
|
||||||
|
override fun getFirCallableContainerFile(symbol: FirCallableSymbol<*>): FirFile? {
|
||||||
|
symbol.overriddenSymbol?.let {
|
||||||
|
return getFirCallableContainerFile(it)
|
||||||
|
}
|
||||||
|
if (symbol is FirAccessorSymbol) {
|
||||||
|
val fir = symbol.fir
|
||||||
|
if (fir is FirSyntheticProperty) {
|
||||||
|
return getFirCallableContainerFile(fir.getter.delegate.symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return providerHelper.getContainingFirFile(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFirFilesByPackage(fqName: FqName): List<FirFile> = error("Should not be called in FIR IDE")
|
||||||
|
|
||||||
|
|
||||||
|
// TODO move out of here
|
||||||
|
// used only for completion
|
||||||
|
fun buildFunctionWithBody(ktNamedFunction: KtNamedFunction): FirFunction<*> {
|
||||||
|
return RawFirBuilder(session, kotlinScopeProvider, stubMode = false).buildFunctionWithBody(ktNamedFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@FirProviderInternals
|
||||||
|
override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
@FirProviderInternals
|
||||||
|
override fun recordGeneratedMember(owner: FirAnnotatedDeclaration, klass: FirDeclaration) {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO this should be reworked because [FirIdeProvider] should not have such method
|
||||||
|
// used only in completion
|
||||||
|
override fun getAllCallableNamesInPackage(fqName: FqName): Set<Name> {
|
||||||
|
return hashSetOf<Name>().apply {
|
||||||
|
indexHelper.getTopLevelPropertiesInPackage(fqName).mapNotNullTo(this) { it.nameAsName }
|
||||||
|
indexHelper.getTopLevelFunctionsInPackage(fqName).mapNotNullTo(this) { it.nameAsName }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val FirSession.firIdeProvider: FirIdeProvider by FirSession.sessionComponentAccessor()
|
||||||
+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.idea.fir.low.level.api.providers
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets
|
||||||
|
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.declarations.FirRegularClass
|
||||||
|
import org.jetbrains.kotlin.fir.psi
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.impl.nestedClassifierScope
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.FirElementFinder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.IndexHelper
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.PackageExistenceChecker
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.executeOrReturnDefaultValueOnPCE
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.KtEnumEntry
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
internal class FirProviderHelper(
|
||||||
|
private val cache: ModuleFileCache,
|
||||||
|
private val firFileBuilder: FirFileBuilder,
|
||||||
|
private val indexHelper: IndexHelper,
|
||||||
|
private val packageExistenceChecker: PackageExistenceChecker,
|
||||||
|
) {
|
||||||
|
fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration<*>? {
|
||||||
|
return executeOrReturnDefaultValueOnPCE(null) {
|
||||||
|
cache.classifierByClassId.computeIfAbsent(classId) {
|
||||||
|
val ktClass = indexHelper.classFromIndexByClassId(classId)
|
||||||
|
?: indexHelper.typeAliasFromIndexByClassId(classId)
|
||||||
|
?: return@computeIfAbsent Optional.empty()
|
||||||
|
if (ktClass is KtEnumEntry) return@computeIfAbsent Optional.empty()
|
||||||
|
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktClass.containingKtFile, cache)
|
||||||
|
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 {
|
||||||
|
indexHelper.getTopLevelFunctions(callableId).mapTo(this) { it.containingKtFile }
|
||||||
|
indexHelper.getTopLevelProperties(callableId).mapTo(this) { it.containingKtFile }
|
||||||
|
}
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
buildList {
|
||||||
|
files.forEach { ktFile ->
|
||||||
|
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, cache)
|
||||||
|
firFile.collectCallableDeclarationsTo(this, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getContainingFirFile(symbol: FirBasedSymbol<*>): FirFile? {
|
||||||
|
val ktFile = symbol.fir.psi?.containingFile as? KtFile ?: return null
|
||||||
|
return cache.getCachedFirFile(ktFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 getNestedClassifierScope(classId: ClassId): FirScope? =
|
||||||
|
(getFirClassifierByFqName(classId) as? FirRegularClass)?.let {
|
||||||
|
nestedClassifierScope(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPackage(fqName: FqName): FqName? =
|
||||||
|
fqName.takeIf(packageExistenceChecker::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.idea.fir.low.level.api.providers
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
internal fun <T: Any> Optional<T>.getOrNull(): T? = orElse(null)
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.sessions
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||||
|
import org.jetbrains.kotlin.fir.java.JavaSymbolProvider
|
||||||
|
import org.jetbrains.kotlin.fir.java.deserialization.KotlinDeserializedJvmSymbolsProvider
|
||||||
|
import org.jetbrains.kotlin.fir.registerCommonComponents
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirBuiltinSymbolProvider
|
||||||
|
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.scopes.KotlinScopeProvider
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.IDEPackagePartProvider
|
||||||
|
import org.jetbrains.kotlin.idea.search.minus
|
||||||
|
import org.jetbrains.kotlin.load.java.JavaClassFinderImpl
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
|
||||||
|
|
||||||
|
class FirIdeModuleLibraryDependenciesSession private constructor(
|
||||||
|
sessionProvider: FirProjectSessionProvider,
|
||||||
|
) : FirSession(sessionProvider) {
|
||||||
|
companion object {
|
||||||
|
fun create(
|
||||||
|
moduleInfo: ModuleSourceInfo,
|
||||||
|
sessionProvider: FirProjectSessionProvider,
|
||||||
|
project: Project,
|
||||||
|
): FirIdeModuleLibraryDependenciesSession {
|
||||||
|
val searchScope = moduleInfo.module.moduleWithLibrariesScope
|
||||||
|
val javaClassFinder = JavaClassFinderImpl().apply {
|
||||||
|
setProjectInstance(project)
|
||||||
|
setScope(searchScope)
|
||||||
|
}
|
||||||
|
val packagePartProvider = IDEPackagePartProvider(searchScope)
|
||||||
|
|
||||||
|
val kotlinClassFinder = VirtualFileFinderFactory.getInstance(project).create(searchScope)
|
||||||
|
return FirIdeModuleLibraryDependenciesSession(sessionProvider).apply {
|
||||||
|
registerCommonComponents()
|
||||||
|
|
||||||
|
val javaSymbolProvider = JavaSymbolProvider(this, sessionProvider.project, searchScope)
|
||||||
|
|
||||||
|
val kotlinScopeProvider = KotlinScopeProvider(::wrapScopeWithJvmMapped)
|
||||||
|
|
||||||
|
register(
|
||||||
|
FirSymbolProvider::class,
|
||||||
|
FirCompositeSymbolProvider(
|
||||||
|
listOf(
|
||||||
|
KotlinDeserializedJvmSymbolsProvider(
|
||||||
|
this,
|
||||||
|
project,
|
||||||
|
packagePartProvider,
|
||||||
|
javaSymbolProvider,
|
||||||
|
kotlinClassFinder,
|
||||||
|
javaClassFinder,
|
||||||
|
kotlinScopeProvider
|
||||||
|
),
|
||||||
|
FirBuiltinSymbolProvider(this, kotlinScopeProvider),
|
||||||
|
FirCloneableSymbolProvider(this, kotlinScopeProvider),
|
||||||
|
javaSymbolProvider,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+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.idea.fir.low.level.api.util
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.psi
|
||||||
|
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
|
||||||
|
object FirElementFinder {
|
||||||
|
inline fun <reified E : FirElement> findElementIn(container: FirElement, crossinline predicate: (E) -> Boolean): E? {
|
||||||
|
var result: E? = null
|
||||||
|
container.accept(object : FirVisitorVoid() {
|
||||||
|
override fun visitElement(element: FirElement) {
|
||||||
|
if (result != null) return
|
||||||
|
if (element is E && predicate(element)) {
|
||||||
|
result = element
|
||||||
|
} else {
|
||||||
|
element.acceptChildren(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified E : FirElement> findElementByPsiIn(container: FirElement, ktElement: KtElement): E? =
|
||||||
|
findElementIn(container) { it.psi === ktElement }
|
||||||
|
}
|
||||||
+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.idea.fir.low.level.api.util
|
||||||
|
|
||||||
|
import com.intellij.openapi.progress.ProcessCanceledException
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.diagnostics.FirDiagnosticHolder
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
|
|
||||||
|
|
||||||
|
internal inline fun <T> executeOrReturnDefaultValueOnPCE(defaultValue: T, action: () -> T): T =
|
||||||
|
try {
|
||||||
|
action()
|
||||||
|
} catch (e: ProcessCanceledException) {
|
||||||
|
defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val FirElement.isErrorElement
|
||||||
|
get() = this is FirDiagnosticHolder
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
internal inline fun <K, V> MutableMap<K, MutableList<V>>.addValueFor(element: K, value: V) {
|
||||||
|
getOrPut(element) { mutableListOf() } += value
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun IdeaModuleInfo.collectTransitiveDependenciesWithSelf(): List<IdeaModuleInfo> {
|
||||||
|
val result = mutableSetOf<IdeaModuleInfo>()
|
||||||
|
fun collect(module: IdeaModuleInfo) {
|
||||||
|
if (module in result) return
|
||||||
|
result += module
|
||||||
|
module.dependencies().forEach(::collect)
|
||||||
|
}
|
||||||
|
collect(this)
|
||||||
|
return result.toList()
|
||||||
|
}
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.idea.fir.low.level.api
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.FirDiagnosticHolder
|
|
||||||
|
|
||||||
internal val FirElement.isErrorElement
|
|
||||||
get() = this is FirDiagnosticHolder
|
|
||||||
+5
-3
@@ -13,11 +13,13 @@ import com.intellij.psi.PsiDocumentManager
|
|||||||
import com.intellij.psi.search.FileTypeIndex
|
import com.intellij.psi.search.FileTypeIndex
|
||||||
import com.intellij.testFramework.LightProjectDescriptor
|
import com.intellij.testFramework.LightProjectDescriptor
|
||||||
import junit.framework.TestCase
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
|
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
|
||||||
import org.jetbrains.kotlin.fir.render
|
import org.jetbrains.kotlin.fir.render
|
||||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.project.productionSourceInfo
|
import org.jetbrains.kotlin.idea.caches.project.productionSourceInfo
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
|
||||||
import org.jetbrains.kotlin.idea.jsonUtils.getString
|
import org.jetbrains.kotlin.idea.jsonUtils.getString
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
||||||
@@ -64,7 +66,7 @@ abstract class AbstractFirLazyResolveTest : KotlinLightCodeInsightFixtureTestCas
|
|||||||
} as KtExpression
|
} as KtExpression
|
||||||
|
|
||||||
val resolveState = expressionToResolve.firResolveState()
|
val resolveState = expressionToResolve.firResolveState()
|
||||||
val resultsDump = when (val firElement = expressionToResolve.getOrBuildFir(resolveState)) {
|
val resultsDump = when (val firElement = resolveState.getOrBuildFirFor(expressionToResolve, FirResolvePhase.BODY_RESOLVE)) {
|
||||||
is FirResolvedImport -> buildString {
|
is FirResolvedImport -> buildString {
|
||||||
append("import ")
|
append("import ")
|
||||||
append(firElement.packageFqName)
|
append(firElement.packageFqName)
|
||||||
@@ -100,9 +102,9 @@ abstract class AbstractFirLazyResolveTest : KotlinLightCodeInsightFixtureTestCas
|
|||||||
val files = FileTypeIndex.getFiles(KotlinFileType.INSTANCE, contentScope)
|
val files = FileTypeIndex.getFiles(KotlinFileType.INSTANCE, contentScope)
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
val psiFile = psiManager.findFile(file) as KtFile
|
val psiFile = psiManager.findFile(file) as KtFile
|
||||||
val session = resolveState.getSession(psiFile)
|
val session = resolveState.firSession
|
||||||
val firProvider = session.firIdeProvider
|
val firProvider = session.firIdeProvider
|
||||||
val firFile = firProvider.getFile(psiFile) ?: continue
|
val firFile = firProvider.cache.getCachedFirFile(psiFile) ?: continue
|
||||||
KotlinTestUtils.assertEqualsToFile(File(expectedTxtPath(file)), firFile.render())
|
KotlinTestUtils.assertEqualsToFile(File(expectedTxtPath(file)), firFile.render())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ fun KtElement.getOrBuildFir(
|
|||||||
phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE
|
phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE
|
||||||
) = LowLevelFirApiFacade.getOrBuildFirFor(this, resolveState, phase)
|
) = LowLevelFirApiFacade.getOrBuildFirFor(this, resolveState, phase)
|
||||||
|
|
||||||
fun KtElement.getFirOfClosestParent() = LowLevelFirApiFacade.getFirOfClosestParent(this)
|
|
||||||
|
|
||||||
inline fun <reified E : FirElement> KtElement.getOrBuildFirSafe(
|
inline fun <reified E : FirElement> KtElement.getOrBuildFirSafe(
|
||||||
resolveState: FirModuleResolveState,
|
resolveState: FirModuleResolveState,
|
||||||
|
|||||||
+3
-4
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
|||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.idea.fir.*
|
import org.jetbrains.kotlin.idea.fir.*
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveState
|
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveState
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.*
|
import org.jetbrains.kotlin.idea.frontend.api.*
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.scopes.KtFirScopeProvider
|
import org.jetbrains.kotlin.idea.frontend.api.fir.scopes.KtFirScopeProvider
|
||||||
@@ -126,7 +125,7 @@ private constructor(
|
|||||||
|
|
||||||
override fun createContextDependentCopy(): KtAnalysisSession {
|
override fun createContextDependentCopy(): KtAnalysisSession {
|
||||||
check(!isContextSession) { "Cannot create context-dependent copy of KtAnalysis session from a context dependent one" }
|
check(!isContextSession) { "Cannot create context-dependent copy of KtAnalysis session from a context dependent one" }
|
||||||
val contextResolveState = LowLevelFirApiFacade.getResolveStateForCompletion(element, firResolveState as FirModuleResolveStateImpl)
|
val contextResolveState = LowLevelFirApiFacade.getResolveStateForCompletion(element, firResolveState)
|
||||||
return KtFirAnalysisSession(
|
return KtFirAnalysisSession(
|
||||||
element,
|
element,
|
||||||
firSession,
|
firSession,
|
||||||
@@ -148,7 +147,7 @@ private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveCall(firCall: FirFunctionCall, callExpression: KtExpression): CallInfo? {
|
private fun resolveCall(firCall: FirFunctionCall, callExpression: KtExpression): CallInfo? {
|
||||||
val session = LowLevelFirApiFacade.getSessionFor(callExpression, firResolveState)
|
val session = firResolveState.firSession
|
||||||
val resolvedFunctionSymbol = firCall.calleeReference.toTargetSymbol(session, firSymbolBuilder)
|
val resolvedFunctionSymbol = firCall.calleeReference.toTargetSymbol(session, firSymbolBuilder)
|
||||||
val resolvedCalleeSymbol = (firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol
|
val resolvedCalleeSymbol = (firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol
|
||||||
return when {
|
return when {
|
||||||
@@ -203,7 +202,7 @@ private constructor(
|
|||||||
@Deprecated("Please use org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSessionProviderKt.analyze")
|
@Deprecated("Please use org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSessionProviderKt.analyze")
|
||||||
fun createForElement(element: KtElement): KtFirAnalysisSession {
|
fun createForElement(element: KtElement): KtFirAnalysisSession {
|
||||||
val firResolveState = LowLevelFirApiFacade.getResolveStateFor(element)
|
val firResolveState = LowLevelFirApiFacade.getResolveStateFor(element)
|
||||||
val firSession = LowLevelFirApiFacade.getSessionFor(element, firResolveState)
|
val firSession = firResolveState.firSession
|
||||||
val project = element.project
|
val project = element.project
|
||||||
val typeContext = ConeTypeCheckerContext(isErrorTypeEqualsToAnything = true, isStubTypeEqualsToAnything = true, firSession)
|
val typeContext = ConeTypeCheckerContext(isErrorTypeEqualsToAnything = true, isStubTypeEqualsToAnything = true, firSession)
|
||||||
val token = ReadActionConfinementValidityToken(project)
|
val token = ReadActionConfinementValidityToken(project)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import com.intellij.openapi.util.io.FileUtil
|
|||||||
import com.intellij.testFramework.LightPlatformTestCase
|
import com.intellij.testFramework.LightPlatformTestCase
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.DuplicatedFirSourceElementsException
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.DuplicatedFirSourceElementsException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
fun <R> executeOnPooledThreadInReadAction(action: () -> R): R =
|
fun <R> executeOnPooledThreadInReadAction(action: () -> R): R =
|
||||||
|
|||||||
+11
-13
@@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.util.application
|
package org.jetbrains.kotlin.idea.util.application
|
||||||
@@ -30,6 +19,15 @@ fun <T> runWriteAction(action: () -> T): T {
|
|||||||
return ApplicationManager.getApplication().runWriteAction<T>(action)
|
return ApplicationManager.getApplication().runWriteAction<T>(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> runWriteActionInEdt(action: () -> T): T {
|
||||||
|
var result: T? = null
|
||||||
|
ApplicationManager.getApplication().invokeLater {
|
||||||
|
result = ApplicationManager.getApplication().runWriteAction<T>(action)
|
||||||
|
}
|
||||||
|
return result!!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun Project.executeWriteCommand(name: String, command: () -> Unit) {
|
fun Project.executeWriteCommand(name: String, command: () -> Unit) {
|
||||||
CommandProcessor.getInstance().executeCommand(this, { runWriteAction(command) }, name, null)
|
CommandProcessor.getInstance().executeCommand(this, { runWriteAction(command) }, name, null)
|
||||||
}
|
}
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.util
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
|
import com.intellij.psi.util.PsiModificationTracker
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a value which will be cached until until any physical PSI change happens
|
||||||
|
* To create one please use [psiModificationTrackerBasedCachedValue]
|
||||||
|
*
|
||||||
|
* @see com.intellij.psi.util.CachedValue
|
||||||
|
* @see com.intellij.psi.util.PsiModificationTracker.MODIFICATION_COUNT
|
||||||
|
*/
|
||||||
|
class PsiModificationTrackerBasedCachedValue<T>(project: Project, createValue: () -> T) : ReadOnlyProperty<Any?, T> {
|
||||||
|
private val cachedValue = CachedValuesManager.getManager(project).createCachedValue {
|
||||||
|
CachedValueProvider.Result(
|
||||||
|
createValue(),
|
||||||
|
PsiModificationTracker.MODIFICATION_COUNT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T = cachedValue.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a value which will be cached until until any physical PSI change happens
|
||||||
|
*
|
||||||
|
* @see com.intellij.psi.util.CachedValue
|
||||||
|
* @see com.intellij.psi.util.PsiModificationTracker.MODIFICATION_COUNT
|
||||||
|
* @see PsiModificationTrackerBasedCachedValue
|
||||||
|
*/
|
||||||
|
fun <T> psiModificationTrackerBasedCachedValue(project: Project, createValue: () -> T): PsiModificationTrackerBasedCachedValue<T> =
|
||||||
|
PsiModificationTrackerBasedCachedValue(project, createValue)
|
||||||
@@ -88,8 +88,7 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
|
|||||||
<applicationService serviceImplementation="org.jetbrains.kotlin.idea.PluginStartupService"/>
|
<applicationService serviceImplementation="org.jetbrains.kotlin.idea.PluginStartupService"/>
|
||||||
<highlightRangeExtension implementation="org.jetbrains.kotlin.idea.fir.highlighter.KotlinFirPsiChecker"/>
|
<highlightRangeExtension implementation="org.jetbrains.kotlin.idea.fir.highlighter.KotlinFirPsiChecker"/>
|
||||||
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.idea.fir.highlighter.KotlinFirPsiChecker"/>
|
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.idea.fir.highlighter.KotlinFirPsiChecker"/>
|
||||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.fir.low.level.api.FirIdeResolveStateService"
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.fir.low.level.api.FirIdeResolveStateService"/>
|
||||||
serviceImplementation="org.jetbrains.kotlin.idea.fir.low.level.api.FirIdeResolveStateServiceImpl"/>
|
|
||||||
<projectService serviceImplementation="org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade"/>
|
<projectService serviceImplementation="org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade"/>
|
||||||
|
|
||||||
<completion.contributor language="kotlin"
|
<completion.contributor language="kotlin"
|
||||||
@@ -157,10 +156,16 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
|
|||||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.framework.LibraryEffectiveKindProvider"
|
<projectService serviceInterface="org.jetbrains.kotlin.idea.framework.LibraryEffectiveKindProvider"
|
||||||
serviceImplementation="org.jetbrains.kotlin.idea.framework.LibraryEffectiveKindProviderImpl"/>
|
serviceImplementation="org.jetbrains.kotlin.idea.framework.LibraryEffectiveKindProviderImpl"/>
|
||||||
|
|
||||||
|
<applicationService
|
||||||
|
serviceInterface="org.jetbrains.kotlin.platform.DefaultIdeTargetPlatformKindProvider"
|
||||||
|
serviceImplementation="org.jetbrains.kotlin.platform.impl.IdeaDefaultIdeTargetPlatformKindProvider"/>
|
||||||
|
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
|
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
|
|
||||||
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"/>
|
||||||
<projectService serviceImplementation="org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener"/>
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener"/>
|
||||||
|
|
||||||
<projectService serviceInterface="org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory"
|
<projectService serviceInterface="org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory"
|
||||||
|
|||||||
Reference in New Issue
Block a user