diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt index c4908b9a497..cf543263b68 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaModuleBasedSession.kt @@ -18,8 +18,10 @@ import org.jetbrains.kotlin.fir.resolve.impl.FirLibrarySymbolProviderImpl class FirJavaModuleBasedSession( moduleInfo: ModuleInfo, override val sessionProvider: FirProjectSessionProvider, - scope: GlobalSearchScope + scope: GlobalSearchScope, + dependenciesProvider: FirSymbolProvider? = null ) : FirModuleBasedSession(moduleInfo) { + init { sessionProvider.sessionCache[moduleInfo] = this registerComponent( @@ -28,7 +30,8 @@ class FirJavaModuleBasedSession( listOf( service(), JavaSymbolProvider(this, sessionProvider.project, scope), - FirDependenciesSymbolProviderImpl(this) + FirLibrarySymbolProviderImpl(this), + dependenciesProvider ?: FirDependenciesSymbolProviderImpl(this) ) ) ) diff --git a/idea/build.gradle.kts b/idea/build.gradle.kts index 4a125080c18..bbb3ae32ddb 100644 --- a/idea/build.gradle.kts +++ b/idea/build.gradle.kts @@ -36,6 +36,8 @@ dependencies { compile(project(":j2k")) compile(project(":idea:formatter")) compile(project(":idea:fir-view")) + compile(project(":compiler:fir:resolve")) + compile(project(":compiler:fir:java")) compile(project(":idea:idea-core")) compile(project(":idea:ide-common")) compile(project(":idea:idea-jps-common")) diff --git a/idea/src/org/jetbrains/kotlin/idea/fir/IdeFirDependenciesSymbolProvider.kt b/idea/src/org/jetbrains/kotlin/idea/fir/IdeFirDependenciesSymbolProvider.kt new file mode 100644 index 00000000000..0a836b6b556 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/fir/IdeFirDependenciesSymbolProvider.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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 + +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.java.FirProjectSessionProvider +import org.jetbrains.kotlin.fir.java.JavaSymbolProvider +import org.jetbrains.kotlin.fir.resolve.AbstractFirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.FirProvider +import org.jetbrains.kotlin.fir.resolve.impl.FirProviderImpl +import org.jetbrains.kotlin.fir.service +import org.jetbrains.kotlin.fir.symbols.CallableId +import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol +import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo +import org.jetbrains.kotlin.idea.caches.project.getModuleInfo +import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex +import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelTypeAliasFqNameIndex +import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtTypeAlias + +class IdeFirDependenciesSymbolProvider( + val moduleInfo: ModuleSourceInfo, + val project: Project, + private val sessionProvider: FirProjectSessionProvider +) : AbstractFirSymbolProvider() { + + // TODO: Our special scope here? + private val depScope = GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(moduleInfo.module) + + private val javaSymbolProvider by lazy { JavaSymbolProvider(sessionProvider.getSession(moduleInfo)!!, project, depScope) } + + + private fun buildKotlinClassOnRequest(file: KtFile, classId: ClassId, session: FirSession): ConeClassLikeSymbol? { + val impl = FirProvider.getInstance(session) as FirProviderImpl + val classifier = impl.getClassLikeSymbolByFqName(classId) + if (classifier != null) { + return classifier + } + + val builder = RawFirBuilder(session, stubMode = true) + impl.recordFile(builder.buildFirFile(file)) + return impl.getClassLikeSymbolByFqName(classId) + } + + private fun selectNearest(classesPsi: Collection, typeAliasesPsi: Collection): KtDeclaration? { + return when { + typeAliasesPsi.isEmpty() -> classesPsi.firstOrNull() + classesPsi.isEmpty() -> typeAliasesPsi.firstOrNull() + else -> (classesPsi.asSequence() + typeAliasesPsi.asSequence()).minWith(Comparator { o1, o2 -> + depScope.compare(o1.containingFile.virtualFile, o2.containingFile.virtualFile) + }) + } + } + + private fun tryKotlin(classId: ClassId): ConeClassLikeSymbol? { + return classCache.lookupCacheOrCalculate(classId) { + val index = KotlinFullClassNameIndex.getInstance() + + val fqNameString = classId.packageFqName.asString() + "." + classId.relativeClassName.asString() + val classesPsi = index[fqNameString, project, depScope] + val typeAliasesPsi = KotlinTopLevelTypeAliasFqNameIndex.getInstance()[fqNameString, project, depScope] + + val psi = selectNearest(classesPsi, typeAliasesPsi) ?: return@lookupCacheOrCalculate null + + val module = psi.getModuleInfo() + if (psi.containingKtFile.isCompiled) { + // TODO: WTF? Resolving libraries in current session + val session = sessionProvider.getSession(moduleInfo) ?: return@lookupCacheOrCalculate null + return@lookupCacheOrCalculate buildKotlinClassOnRequest(psi.containingKtFile, classId, session) + } + + val session = sessionProvider.getSession(module) ?: return@lookupCacheOrCalculate null + session.service().getClassLikeSymbolByFqName(classId) + } + } + + private fun tryJava(classId: ClassId): ConeClassLikeSymbol? { + return javaSymbolProvider.getClassLikeSymbolByFqName(classId) + } + + override fun getClassLikeSymbolByFqName(classId: ClassId): ConeClassLikeSymbol? { + return tryKotlin(classId) ?: tryJava(classId) + } + + override fun getCallableSymbols(callableId: CallableId): List { + // TODO + return emptyList() + } + + override fun getPackage(fqName: FqName): FqName? { + + if (PackageIndexUtil.packageExists(fqName, depScope, project)) { + return fqName + } + return javaSymbolProvider.getPackage(fqName) + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/fir/FirTotalKotlinResolveInIdeTest.kt b/idea/tests/org/jetbrains/kotlin/idea/fir/FirTotalKotlinResolveInIdeTest.kt index 0e2282a6106..ae030e755fa 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/fir/FirTotalKotlinResolveInIdeTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/fir/FirTotalKotlinResolveInIdeTest.kt @@ -14,18 +14,14 @@ import com.intellij.testFramework.ModuleTestCase import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.builder.RawFirBuilder import org.jetbrains.kotlin.fir.declarations.FirFile -import org.jetbrains.kotlin.fir.dependenciesWithoutSelf import org.jetbrains.kotlin.fir.doFirResolveTestBench import org.jetbrains.kotlin.fir.java.FirJavaModuleBasedSession -import org.jetbrains.kotlin.fir.java.FirLibrarySession import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider import org.jetbrains.kotlin.fir.resolve.FirProvider import org.jetbrains.kotlin.fir.resolve.impl.FirProviderImpl import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveTransformer import org.jetbrains.kotlin.fir.service import org.jetbrains.kotlin.idea.KotlinFileType -import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo -import org.jetbrains.kotlin.idea.caches.project.isLibraryClasses import org.jetbrains.kotlin.idea.caches.project.productionSourceInfo import org.jetbrains.kotlin.psi.KtFile import java.io.File @@ -60,13 +56,11 @@ class FirTotalKotlinResolveInIdeTest : ModuleTestCase() { private fun createSession(): FirSession { val moduleInfo = module.productionSourceInfo()!! val sessionProvider = FirProjectSessionProvider(project) - moduleInfo.dependenciesWithoutSelf().forEach { - if (it is IdeaModuleInfo && it.isLibraryClasses()) { - FirLibrarySession(it, sessionProvider, it.contentScope()) - } - } - return FirJavaModuleBasedSession(moduleInfo, sessionProvider, moduleInfo.contentScope()) + return FirJavaModuleBasedSession( + moduleInfo, sessionProvider, moduleInfo.contentScope(), + IdeFirDependenciesSymbolProvider(moduleInfo, project, sessionProvider) + ) } fun testTotalKotlin() { diff --git a/prepare/idea-plugin/build.gradle.kts b/prepare/idea-plugin/build.gradle.kts index 2e214682623..f057fa83f0c 100644 --- a/prepare/idea-plugin/build.gradle.kts +++ b/prepare/idea-plugin/build.gradle.kts @@ -35,6 +35,7 @@ val projectsToShadow by extra(listOf( ":compiler:fir:cones", ":compiler:fir:resolve", ":compiler:fir:tree", + ":compiler:fir:java", ":compiler:fir:psi2fir", ":idea:fir-view" )