[AA, LL] use stubs to build deserialized Fir elements in IDE

^KTIJ-24638

Notice on `DebugSymbolRenderer`:
stub based deserializer sets source directly,
but it's available in IDE mode only.
Thus, standalone and IDE tests have different results.
In order to avoid this, sources for compiled code are explicitly ignored

Notice on distinct callables:
for a file which belong to multiple libraries, decompiled code would be build per library.
In order to avoid ambiguity errors for members in that file,
we need to distinct provided elements by origins
failed test from IJ repo:
 FirReferenceResolveWithCrossLibTestGenerated#testSetWithTypeParameters
This commit is contained in:
Anna Kozlova
2023-02-15 14:22:59 +01:00
committed by teamcity
parent 96c15f6c71
commit d59d66e876
41 changed files with 1957 additions and 90 deletions
@@ -42,6 +42,8 @@ public abstract class KotlinDeclarationProvider {
public abstract fun findInternalFilesForFacade(facadeFqName: FqName): Collection<KtFile>
public abstract fun findFilesForScript(scriptFqName: FqName): Collection<KtScript>
public abstract fun computePackageSetWithTopLevelCallableDeclarations(): Set<String>
}
public abstract class KotlinDeclarationProviderFactory {
@@ -5,24 +5,34 @@
package org.jetbrains.kotlin.analysis.providers.impl
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileVisitor
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.SingleRootFileViewProvider
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.StubElement
import com.intellij.util.containers.CollectionFactory
import com.intellij.util.indexing.FileContent
import com.intellij.util.indexing.FileContentImpl
import com.intellij.util.io.URLUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInDecompiler
import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache
import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinClsStubBuilder
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.psi.stubs.impl.*
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
@@ -84,6 +94,11 @@ public class KotlinStaticDeclarationProvider internal constructor(
return index.scriptMap[scriptFqName].orEmpty().filter { it.containingKtFile.virtualFile in scope }
}
override fun computePackageSetWithTopLevelCallableDeclarations(): Set<String> {
val packageNames = index.topLevelPropertyMap.keys + index.topLevelFunctionMap.keys
return packageNames.mapTo(mutableSetOf()) { it.asString() }
}
override fun getTopLevelProperties(callableId: CallableId): Collection<KtProperty> =
index.topLevelPropertyMap[callableId.packageName]
?.filter { ktProperty ->
@@ -109,6 +124,7 @@ public class KotlinStaticDeclarationProviderFactory(
private val project: Project,
files: Collection<KtFile>,
private val jarFileSystem: CoreJarFileSystem = CoreJarFileSystem(),
additionalRoots: List<VirtualFile> = emptyList(),
) : KotlinDeclarationProviderFactory() {
private val index = KotlinStaticDeclarationIndex()
@@ -247,13 +263,23 @@ public class KotlinStaticDeclarationProviderFactory(
// member functions and properties
stub.childrenStubs.forEach(::indexStub)
}
is KotlinObjectStubImpl -> {
addToClassMap(stub.psi)
// member functions and properties
stub.childrenStubs.forEach(::indexStub)
}
is KotlinTypeAliasStubImpl -> addToTypeAliasMap(stub.psi)
is KotlinFunctionStubImpl -> addToFunctionMap(stub.psi)
is KotlinPropertyStubImpl -> addToPropertyMap(stub.psi)
is KotlinPlaceHolderStubImpl -> {
if (stub.stubType == KtStubElementTypes.CLASS_BODY) {
stub.getChildrenStubs().filterIsInstance<KotlinClassOrObjectStub<*>>().forEach(::indexStub)
}
}
}
}
loadBuiltIns().forEach { ktFileStub ->
fun processStub(ktFileStub: KotlinFileStubImpl) {
val ktFile: KtFile = ktFileStub.psi
addToFacadeFileMap(ktFile)
@@ -270,6 +296,32 @@ public class KotlinStaticDeclarationProviderFactory(
ktFileStub.childrenStubs.forEach(::indexStub)
}
loadBuiltIns().forEach { processStub(it) }
val binaryClassCache = ClsKotlinBinaryClassCache.getInstance()
for (root in additionalRoots) {
KotlinFakeClsStubsCache.processAdditionalRoot(root) { additionalRoot ->
val stubs = mutableListOf<KotlinFileStubImpl>()
VfsUtilCore.visitChildrenRecursively(additionalRoot, object : VirtualFileVisitor<Void>() {
override fun visitFile(file: VirtualFile): Boolean {
if (!file.isDirectory && file.fileType == JavaClassFileType.INSTANCE) {
val fileContent = FileContentImpl.createByFile(file)
if (!binaryClassCache.isKotlinJvmCompiledFile(file, fileContent.content)) return true
val stub = KotlinClsStubBuilder().buildFileStub(fileContent) as? KotlinFileStubImpl ?: return true
val fakeFile = object : KtFile(KtClassFileViewProvider(psiManager, fileContent.file), isCompiled = true) {
override fun getStub() = stub
override fun isPhysical() = false
}
stub.psi = fakeFile
stubs.add(stub)
}
return true
}
})
stubs
}?.forEach { processStub(it) }
}
// Indexing user source files
files.forEach {
it.accept(recorder)
@@ -280,3 +332,24 @@ public class KotlinStaticDeclarationProviderFactory(
return KotlinStaticDeclarationProvider(index, searchScope)
}
}
/**
* Test application service to store stubs of shared between tests libraries.
*
* Otherwise, each test would start indexing of stdlib from scratch,
* and under the lock which makes tests extremely slow*/
class KotlinFakeClsStubsCache {
private val fakeFileClsStubs = CollectionFactory.createConcurrentWeakValueMap<String, List<KotlinFileStubImpl>>()
companion object {
fun processAdditionalRoot(root: VirtualFile, storage: (VirtualFile) -> List<KotlinFileStubImpl>): List<KotlinFileStubImpl>? {
val service = ApplicationManager.getApplication().getService(KotlinFakeClsStubsCache::class.java) ?: return null
if (service.fakeFileClsStubs[root.path] == null) {
service.fakeFileClsStubs[root.path] = storage(root)
}
return service.fakeFileClsStubs.computeIfAbsent(root.path) { _ ->
storage(root)
}
}
}
}