diff --git a/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/fe10/test/configurator/AnalysisApiFe10TestConfigurator.kt b/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/fe10/test/configurator/AnalysisApiFe10TestConfigurator.kt index 99b8f413a71..e5f21ea7441 100644 --- a/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/fe10/test/configurator/AnalysisApiFe10TestConfigurator.kt +++ b/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/fe10/test/configurator/AnalysisApiFe10TestConfigurator.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.psi.PsiFile import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtModuleFactory import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtSourceModuleFactory @@ -43,6 +44,7 @@ object AnalysisApiFe10TestConfigurator : AnalysisApiTestConfigurator() { override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFe10TestServiceRegistrar, ) diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibraryBinaryTestConfigurator.kt b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibraryBinaryTestConfigurator.kt index ed7836a6e71..e0e20862659 100644 --- a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibraryBinaryTestConfigurator.kt +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibraryBinaryTestConfigurator.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiLibraryBaseTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure @@ -47,6 +48,7 @@ object AnalysisApiFirLibraryBinaryTestConfigurator : AnalysisApiTestConfigurator override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFirTestServiceRegistrar, AnalysisApiLibraryBaseTestServiceRegistrar, ) diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibrarySourceTestConfigurator.kt b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibrarySourceTestConfigurator.kt index 73ec8ab5dcd..633a1b0fe28 100644 --- a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibrarySourceTestConfigurator.kt +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/test/configurators/library/AnalysisApiFirLibrarySourceTestConfigurator.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiLibraryBaseTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure @@ -57,6 +58,7 @@ object AnalysisApiFirLibrarySourceTestConfigurator : AnalysisApiTestConfigurator override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFirTestServiceRegistrar, AnalysisApiLibraryBaseTestServiceRegistrar, ) diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt index d4d9c3ca8ad..2804bba9393 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt @@ -12,12 +12,17 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals import org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService import org.jetbrains.kotlin.analysis.api.lifetime.KtDefaultLifetimeTokenProvider import org.jetbrains.kotlin.analysis.api.lifetime.KtReadActionConfinementDefaultLifetimeTokenProvider +import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory +import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache +import org.jetbrains.kotlin.analysis.decompiler.stub.file.FileAttributeService +import org.jetbrains.kotlin.analysis.decompiler.stub.files.DummyFileAttributeService import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl import org.jetbrains.kotlin.analysis.providers.* import org.jetbrains.kotlin.analysis.providers.impl.* import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider +import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar import org.jetbrains.kotlin.psi.KotlinReferenceProvidersService import org.jetbrains.kotlin.psi.KtFile @@ -45,17 +50,28 @@ object AnalysisApiBaseTestServiceRegistrar: AnalysisApiTestServiceRegistrar() { } override fun registerProjectModelServices(project: MockProject, testServices: TestServices) { - val allKtFiles = testServices.ktModuleProvider.getModuleStructure().mainModules.flatMap { it.files.filterIsInstance() } - + val moduleStructure = testServices.ktModuleProvider.getModuleStructure() + val allKtFiles = moduleStructure.mainModules.flatMap { it.files.filterIsInstance() } + val roots = StandaloneProjectFactory.getVirtualFilesForLibraryRoots( + moduleStructure.binaryModules.flatMap { binary -> binary.getBinaryRoots() }, + testServices.environmentManager.getProjectEnvironment() + ).distinct() project.apply { registerService(KtModuleScopeProvider::class.java, KtModuleScopeProviderImpl()) registerService(KotlinAnnotationsResolverFactory::class.java, KotlinStaticAnnotationsResolverFactory(allKtFiles)) - registerService(KotlinDeclarationProviderFactory::class.java, KotlinStaticDeclarationProviderFactory(project, allKtFiles)) + + registerService(KotlinDeclarationProviderFactory::class.java, KotlinStaticDeclarationProviderFactory( + project, + allKtFiles, + additionalRoots = roots + )) registerService(KotlinPackageProviderFactory::class.java, KotlinStaticPackageProviderFactory(project, allKtFiles)) registerService(KotlinReferenceProvidersService::class.java, HLApiReferenceProviderService::class.java) registerService(KotlinResolutionScopeProvider::class.java, KotlinByModulesResolutionScopeProvider::class.java) } } - override fun registerApplicationServices(application: MockApplication, testServices: TestServices) {} + override fun registerApplicationServices(application: MockApplication, testServices: TestServices) { + application.registerService(KotlinFakeClsStubsCache::class.java, KotlinFakeClsStubsCache()) + } } diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiDecompiledCodeTestServiceRegistrar.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiDecompiledCodeTestServiceRegistrar.kt new file mode 100644 index 00000000000..9abf56d4d73 --- /dev/null +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiDecompiledCodeTestServiceRegistrar.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.api.impl.base.test.configurators + +import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.mock.MockApplication +import com.intellij.mock.MockProject +import com.intellij.openapi.extensions.LoadingOrder +import com.intellij.psi.ClassFileViewProviderFactory +import com.intellij.psi.FileTypeFileViewProviders +import com.intellij.psi.compiled.ClassFileDecompilers +import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInDecompiler +import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinClassFileDecompiler +import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache +import org.jetbrains.kotlin.analysis.decompiler.stub.file.FileAttributeService +import org.jetbrains.kotlin.analysis.decompiler.stub.files.DummyFileAttributeService +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar +import org.jetbrains.kotlin.test.TestInfrastructureInternals +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.analysis.test.framework.services.disposableProvider + + +object AnalysisApiDecompiledCodeTestServiceRegistrar : AnalysisApiTestServiceRegistrar() { + override fun registerProjectExtensionPoints(project: MockProject, testServices: TestServices) { + } + + override fun registerProjectServices(project: MockProject, testServices: TestServices) { + } + + override fun registerProjectModelServices(project: MockProject, testServices: TestServices) { + } + + override fun registerApplicationServices(application: MockApplication, testServices: TestServices) { + application.apply { + registerService(ClsKotlinBinaryClassCache::class.java) + registerService(FileAttributeService::class.java, DummyFileAttributeService) + } + } +} \ No newline at end of file diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiLibraryBaseTestServiceRegistrar.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiLibraryBaseTestServiceRegistrar.kt index 78fd841c4c6..3f6ef16a8f1 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiLibraryBaseTestServiceRegistrar.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiLibraryBaseTestServiceRegistrar.kt @@ -34,11 +34,6 @@ object AnalysisApiLibraryBaseTestServiceRegistrar : AnalysisApiTestServiceRegist } override fun registerApplicationServices(application: MockApplication, testServices: TestServices) { - application.apply { - registerService(ClsKotlinBinaryClassCache::class.java) - registerService(FileAttributeService::class.java, DummyFileAttributeService) - } - FileTypeFileViewProviders.INSTANCE.addExplicitExtension(JavaClassFileType.INSTANCE, ClassFileViewProviderFactory()) ClassFileDecompilers.getInstance().EP_NAME.point.apply { diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt index 1ff572adb5b..86e82a62dd8 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt @@ -42,6 +42,8 @@ public abstract class KotlinDeclarationProvider { public abstract fun findInternalFilesForFacade(facadeFqName: FqName): Collection public abstract fun findFilesForScript(scriptFqName: FqName): Collection + + public abstract fun computePackageSetWithTopLevelCallableDeclarations(): Set } public abstract class KotlinDeclarationProviderFactory { diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt index 3a3b97a528e..6a828aeca2c 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt @@ -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 { + val packageNames = index.topLevelPropertyMap.keys + index.topLevelFunctionMap.keys + return packageNames.mapTo(mutableSetOf()) { it.asString() } + } + override fun getTopLevelProperties(callableId: CallableId): Collection = index.topLevelPropertyMap[callableId.packageName] ?.filter { ktProperty -> @@ -109,6 +124,7 @@ public class KotlinStaticDeclarationProviderFactory( private val project: Project, files: Collection, private val jarFileSystem: CoreJarFileSystem = CoreJarFileSystem(), + additionalRoots: List = 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>().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() + VfsUtilCore.visitChildrenRecursively(additionalRoot, object : VirtualFileVisitor() { + 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>() + + companion object { + fun processAdditionalRoot(root: VirtualFile, storage: (VirtualFile) -> List): List? { + 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) + } + } + } +} diff --git a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt index 6ad60476c8b..da7e39924f8 100644 --- a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt +++ b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt @@ -25,6 +25,10 @@ class KtStaticModuleProvider( containingFileAsVirtualFile.analysisExtensionFileContextModule?.let { return it } + projectStructure.binaryModules.firstOrNull { binaryModule -> + containingFileAsVirtualFile in binaryModule.contentScope + }?.let { return it } + return projectStructure.mainModules .first { module -> element in module.ktModule.contentScope diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt index 6e58dae80d2..91cff126bbd 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.JvmFirDeserializedSymbolProviderFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirBuiltinsSessionFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionConfigurator import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider @@ -167,6 +168,7 @@ public class StandaloneAnalysisAPISessionBuilder( registerService(ClsJavaStubByVirtualFileCache::class.java, ClsJavaStubByVirtualFileCache()) registerService(KotlinAsJavaSupport::class.java, SymbolKotlinAsJavaSupport(this)) registerService(ReadWriteAccessChecker::class.java, ReadWriteAccessCheckerFirImpl()) + registerService(JvmFirDeserializedSymbolProviderFactory::class.java, JvmFirDeserializedSymbolProviderFactory()) } @Suppress("DEPRECATION") diff --git a/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt index f2b344394a9..ba905170d7e 100644 --- a/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt +++ b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt @@ -10,9 +10,12 @@ import com.intellij.mock.MockProject import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtStaticModuleProvider import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.JvmFirDeserializedSymbolProviderFactory import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider +import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory import org.jetbrains.kotlin.analysis.providers.KotlinPsiDeclarationProviderFactory +import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticDeclarationProviderFactory import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticPsiDeclarationProviderFactory import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar @@ -41,6 +44,7 @@ object StandaloneModeTestServiceRegistrar : AnalysisApiTestServiceRegistrar() { projectEnvironment.environment.jarFileSystem as CoreJarFileSystem ) ) + registerService(JvmFirDeserializedSymbolProviderFactory::class.java, JvmFirDeserializedSymbolProviderFactory::class.java) } } diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt index 98eda7efb3f..f9ea551a7e5 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt @@ -266,7 +266,11 @@ public class DebugSymbolRenderer( withIndent { appendLine().append("psi: ") - renderValue(call.psi?.javaClass?.simpleName, renderSymbolsFully = false) + val psi = + if (call.psi?.containingKtFile?.isCompiled == true) { + null + } else call.psi + renderValue(psi?.javaClass?.simpleName, renderSymbolsFully = false) } } diff --git a/analysis/analysis-api/testData/symbols/symbolByFqName/classFromJdk.descriptors.txt b/analysis/analysis-api/testData/symbols/symbolByFqName/classFromJdk.descriptors.txt deleted file mode 100644 index 94339479c70..00000000000 --- a/analysis/analysis-api/testData/symbols/symbolByFqName/classFromJdk.descriptors.txt +++ /dev/null @@ -1,43 +0,0 @@ -KtNamedClassOrObjectSymbol: - annotationsList: [] - classIdIfNonLocal: java/lang/String - classKind: CLASS - companionObject: null - contextReceivers: [] - isData: false - isExternal: false - isFun: false - isInline: false - isInner: false - modality: FINAL - name: String - origin: JAVA - superTypes: [ - KtUsualClassType: - annotationsList: [] - ownTypeArguments: [] - type: kotlin/Any - KtUsualClassType: - annotationsList: [] - ownTypeArguments: [] - type: java/io/Serializable - KtUsualClassType: - annotationsList: [] - ownTypeArguments: [ - KtFlexibleType: - annotationsList: [] - type: kotlin/String! - ] - type: kotlin/Comparable - KtUsualClassType: - annotationsList: [] - ownTypeArguments: [] - type: kotlin/CharSequence - ] - symbolKind: TOP_LEVEL - typeParameters: [] - visibility: Public - getContainingModule: Could not render due to java.util.NoSuchElementException: Collection contains no element matching the predicate. - - annotationApplicableTargets: null - deprecationStatus: null \ No newline at end of file diff --git a/analysis/analysis-api/testData/symbols/symbolByReference/constructorViaTypeAlias.descriptors.txt b/analysis/analysis-api/testData/symbols/symbolByReference/constructorViaTypeAlias.descriptors.txt index 465bc7f49ea..625a7edbba8 100644 --- a/analysis/analysis-api/testData/symbols/symbolByReference/constructorViaTypeAlias.descriptors.txt +++ b/analysis/analysis-api/testData/symbols/symbolByReference/constructorViaTypeAlias.descriptors.txt @@ -40,11 +40,9 @@ KtConstructorSymbol: type: (kotlin/collections/MutableCollection..kotlin/collections/Collection?) symbolKind: LOCAL typeParameters: [] - getContainingModule: Could not render due to java.util.NoSuchElementException: Collection contains no element matching the predicate. - + getContainingModule: KtSdkModule "SDK jdk" deprecationStatus: null ] visibility: Public - getContainingModule: Could not render due to java.util.NoSuchElementException: Collection contains no element matching the predicate. - + getContainingModule: KtSdkModule "SDK jdk" deprecationStatus: null \ No newline at end of file diff --git a/analysis/analysis-api/testData/symbols/symbolByReference/samConstructor.descriptors.txt b/analysis/analysis-api/testData/symbols/symbolByReference/samConstructor.descriptors.txt index 980a94efa6b..47820a24fd6 100644 --- a/analysis/analysis-api/testData/symbols/symbolByReference/samConstructor.descriptors.txt +++ b/analysis/analysis-api/testData/symbols/symbolByReference/samConstructor.descriptors.txt @@ -43,6 +43,5 @@ KtSamConstructorSymbol: deprecationStatus: null ] - getContainingModule: Could not render due to java.util.NoSuchElementException: Collection contains no element matching the predicate. - + getContainingModule: KtSdkModule "SDK jdk" deprecationStatus: null \ No newline at end of file diff --git a/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/clsStubBuilding.kt b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/clsStubBuilding.kt index df7522253b5..d1d3378dba7 100644 --- a/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/clsStubBuilding.kt +++ b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/clsStubBuilding.kt @@ -141,6 +141,7 @@ fun createStubForTypeName( val segments = fqName.pathSegments().asReversed() assert(segments.isNotEmpty()) + val packageLength = if (substituteWithAny) 1 else typeClassId.packageFqName.pathSegments().size fun recCreateStubForType(current: StubElement, level: Int): KotlinUserTypeStub { val lastSegment = segments[level] @@ -148,7 +149,7 @@ fun createStubForTypeName( if (level + 1 < segments.size) { recCreateStubForType(userTypeStub, level + 1) } - KotlinNameReferenceExpressionStubImpl(userTypeStub, lastSegment.ref()) + KotlinNameReferenceExpressionStubImpl(userTypeStub, lastSegment.ref(), level < segments.size - packageLength) if (!substituteWithAny) { bindTypeArguments(userTypeStub, level) } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/CompositeKotlinDeclarationProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/CompositeKotlinDeclarationProvider.kt index 96c6f186338..3446921147d 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/CompositeKotlinDeclarationProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/CompositeKotlinDeclarationProvider.kt @@ -65,6 +65,10 @@ private constructor( return providers.flatMapTo(mutableListOf()) { it.findFilesForScript(scriptFqName) } } + override fun computePackageSetWithTopLevelCallableDeclarations(): Set { + return providers.flatMapTo(mutableSetOf()) { it.computePackageSetWithTopLevelCallableDeclarations() } + } + companion object { fun create(providers: List): KotlinDeclarationProvider { return when (providers.size) { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/EmptyKotlinDeclarationProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/EmptyKotlinDeclarationProvider.kt index 3cc5eb67d57..043790c86fb 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/EmptyKotlinDeclarationProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/EmptyKotlinDeclarationProvider.kt @@ -25,4 +25,5 @@ internal object EmptyKotlinDeclarationProvider : KotlinDeclarationProvider() { override fun findFilesForFacade(facadeFqName: FqName) = emptyList() override fun findInternalFilesForFacade(facadeFqName: FqName) = emptyList() override fun findFilesForScript(scriptFqName: FqName) = emptyList() + override fun computePackageSetWithTopLevelCallableDeclarations(): Set = emptySet() } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/FileBasedKotlinDeclarationProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/FileBasedKotlinDeclarationProvider.kt index 15f53209e09..303af4bf835 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/FileBasedKotlinDeclarationProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/FileBasedKotlinDeclarationProvider.kt @@ -129,6 +129,9 @@ internal class FileBasedKotlinDeclarationProvider(val kotlinFile: KtFile) : Kotl } override fun findInternalFilesForFacade(facadeFqName: FqName): Collection = emptyList() + override fun computePackageSetWithTopLevelCallableDeclarations(): Set { + return setOf(kotlinFile.packageFqName.asString()) + } override fun findFilesForScript(scriptFqName: FqName): Collection = listOfNotNull(kotlinFile.script?.takeIf { it.fqName == scriptFqName }) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/JvmFirDeserializedSymbolProviderFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/JvmFirDeserializedSymbolProviderFactory.kt new file mode 100644 index 00000000000..ceb104f0361 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/JvmFirDeserializedSymbolProviderFactory.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider +import org.jetbrains.kotlin.fir.java.FirJavaFacade +import org.jetbrains.kotlin.fir.java.deserialization.JvmClassFileBasedSymbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory + +open class JvmFirDeserializedSymbolProviderFactory { + open fun createJvmFirDeserializedSymbolProviders( + project: Project, + session: FirSession, + moduleData: LLFirModuleData, + kotlinScopeProvider: FirKotlinScopeProvider, + moduleDataProvider: SingleModuleDataProvider, + firJavaFacade: FirJavaFacade, + packagePartProvider: PackagePartProvider, + scope: GlobalSearchScope + ): List { + return listOf( + JvmClassFileBasedSymbolProvider( + session, + moduleDataProvider, + kotlinScopeProvider, + packagePartProvider, + VirtualFileFinderFactory.getInstance(project).create(scope), + firJavaFacade + ) + ) + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt index c1a78f5c89a..344ef802919 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/LLFirLibraryProviderFactory.kt @@ -11,8 +11,10 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirDependenci import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirJavaFacadeForBinaries import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.JvmStubBasedFirDeserializedSymbolProvider import org.jetbrains.kotlin.analysis.providers.createPackagePartProvider import org.jetbrains.kotlin.fir.BuiltinTypes +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider import org.jetbrains.kotlin.fir.java.deserialization.JvmClassFileBasedSymbolProvider import org.jetbrains.kotlin.fir.java.deserialization.OptionalAnnotationClassesProvider @@ -20,6 +22,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.load.java.createJavaClassFinder import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory +import org.jetbrains.kotlin.utils.addIfNotNull internal object LLFirLibraryProviderFactory { fun createLibraryProvidersForScope( @@ -31,21 +34,9 @@ internal object LLFirLibraryProviderFactory { scope: GlobalSearchScope, builtinSymbolProvider: FirSymbolProvider, ): LLFirModuleWithDependenciesSymbolProvider { - val moduleDataProvider = SingleModuleDataProvider(moduleData) - val packagePartProvider = project.createPackagePartProvider(scope) return LLFirModuleWithDependenciesSymbolProvider( session, - providers = listOfNotNull( - JvmClassFileBasedSymbolProvider( - session, - moduleDataProvider, - kotlinScopeProvider, - packagePartProvider, - VirtualFileFinderFactory.getInstance(project).create(scope), - LLFirJavaFacadeForBinaries(session, builtinTypes, project.createJavaClassFinder(scope), moduleDataProvider) - ), - OptionalAnnotationClassesProvider.createIfNeeded(session, moduleDataProvider, kotlinScopeProvider, packagePartProvider), - ), + providers = createLibraryProvidersForAllProjectLibraries(session, moduleData, kotlinScopeProvider, project, builtinTypes, scope), LLFirDependenciesSymbolProvider(session, listOf(builtinSymbolProvider)), ) } @@ -60,16 +51,22 @@ internal object LLFirLibraryProviderFactory { ): List { val moduleDataProvider = SingleModuleDataProvider(moduleData) val packagePartProvider = project.createPackagePartProvider(scope) - return listOfNotNull( - JvmClassFileBasedSymbolProvider( - session, - moduleDataProvider, - kotlinScopeProvider, - packagePartProvider, - VirtualFileFinderFactory.getInstance(project).create(scope), - LLFirJavaFacadeForBinaries(session, builtinTypes, project.createJavaClassFinder(scope), moduleDataProvider) - ), - OptionalAnnotationClassesProvider.createIfNeeded(session, moduleDataProvider, kotlinScopeProvider, packagePartProvider), - ) + return buildList { + val firJavaFacade = LLFirJavaFacadeForBinaries(session, builtinTypes, project.createJavaClassFinder(scope), moduleDataProvider) + val service = project.getService(JvmFirDeserializedSymbolProviderFactory::class.java) + addAll( + service.createJvmFirDeserializedSymbolProviders( + project, + session, + moduleData, + kotlinScopeProvider, + moduleDataProvider, + firJavaFacade, + packagePartProvider, + scope + ) + ) + addIfNotNull(OptionalAnnotationClassesProvider.createIfNeeded(session, moduleDataProvider, kotlinScopeProvider, packagePartProvider)) + } } } \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt index 58b20561a3d..e3c4f978724 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt @@ -94,7 +94,7 @@ internal fun LLFirSourcesSession.registerCompilerPluginServices( } internal fun createJavaSymbolProvider( - firSession: LLFirSession, + firSession: FirSession, moduleData: LLFirModuleData, project: Project, contentScope: GlobalSearchScope diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt index 5e4a6db46c1..7b76f3abbcd 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirModuleWithDependenciesSymbolProvider.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.load.kotlin.FacadeClassSource import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -161,7 +162,7 @@ internal class LLFirDependenciesSymbolProvider( } private fun FirCallableSymbol<*>.jvmClassName(): JvmClassName? { - val jvmPackagePartSource = fir.containerSource as? JvmPackagePartSource ?: return null + val jvmPackagePartSource = fir.containerSource as? FacadeClassSource ?: return null return jvmPackagePartSource.facadeClassName ?: jvmPackagePartSource.className } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt index cdf839b72cf..83b720fdcfc 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt @@ -238,6 +238,10 @@ class LLFirResolveExtensionToolDeclarationProvider internal constructor( .mapNotNullTo(mutableListOf()) { it.kotlinFile.script } } + override fun computePackageSetWithTopLevelCallableDeclarations(): Set { + return emptySet() //todo + } + private inline fun getDeclarationProvidersByPackage( packageFqName: FqName, crossinline filter: (KtResolveExtensionFile) -> Boolean diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmFromStubDecompilerSource.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmFromStubDecompilerSource.kt new file mode 100644 index 00000000000..c949ff9ab95 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmFromStubDecompilerSource.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.descriptors.SourceFile +import org.jetbrains.kotlin.load.kotlin.FacadeClassSource +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerAbiStability +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource + +//required for LLFirDependenciesSymbolProvider#jvmClassName, to resolve ambiguities +//todo check if moving builtins to stubs would solve the issue +class JvmFromStubDecompilerSource( + override val className: JvmClassName, + override val facadeClassName: JvmClassName? = null, + override val incompatibility: IncompatibleVersionErrorData? = null, + override val isPreReleaseInvisible: Boolean = false, + override val abiStability: DeserializedContainerAbiStability = DeserializedContainerAbiStability.STABLE, +) : DeserializedContainerSource, FacadeClassSource { + constructor(packageName: FqName) : + this(JvmClassName.byClassId(ClassId.topLevel(JvmClassName.byInternalName(packageName.asString()).fqNameForTopLevelClassMaybeWithDollars))) + + override fun getContainingFile(): SourceFile { + return SourceFile.NO_SOURCE_FILE + } + + override val presentableString: String + get() = className.internalName +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedDeserializedSymbolProviderFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedDeserializedSymbolProviderFactory.kt new file mode 100644 index 00000000000..fb57d342922 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedDeserializedSymbolProviderFactory.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization + +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.search.DelegatingGlobalSearchScope +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.JvmFirDeserializedSymbolProviderFactory +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.createJavaSymbolProvider +import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider +import org.jetbrains.kotlin.fir.java.FirJavaFacade +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol + +class JvmStubBasedDeserializedSymbolProviderFactory : JvmFirDeserializedSymbolProviderFactory() { + override fun createJvmFirDeserializedSymbolProviders( + project: Project, + session: FirSession, + moduleData: LLFirModuleData, + kotlinScopeProvider: FirKotlinScopeProvider, + moduleDataProvider: SingleModuleDataProvider, + firJavaFacade: FirJavaFacade, + packagePartProvider: PackagePartProvider, + scope: GlobalSearchScope + ): List { + return buildList { + //stub based provider here works over kotlin-only indices and thus provides only kotlin declarations + //in order to find java declarations, one need to explicitly setup java symbol provider. + //for ProtoBuf based provider (used in compiler), there is no need in separated java provider, + //because all declarations are retrieved at once and are not distinguished + add( + JvmStubBasedFirDeserializedSymbolProvider( + session, + moduleDataProvider, + kotlinScopeProvider, + project.createDeclarationProvider(object : DelegatingGlobalSearchScope(project, scope) { + override fun contains(file: VirtualFile): Boolean { + if (file.extension == BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION) return false + return super.contains(file) + } + }) + ) + ) + add(createJavaSymbolProvider(session, moduleData, project, scope)) + } + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt new file mode 100644 index 00000000000..fba7b9a6398 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt @@ -0,0 +1,209 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.LLFirKotlinSymbolProviderNameCache +import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.FirCache +import org.jetbrains.kotlin.fir.caches.createCache +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider +import org.jetbrains.kotlin.fir.java.deserialization.KotlinBuiltins +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals +import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtTypeAlias +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment + +typealias DeserializedTypeAliasPostProcessor = (FirTypeAliasSymbol) -> Unit + +/** + * [JvmStubBasedFirDeserializedSymbolProvider] works over existing stubs, + * retrieving them by classId/callableId from [KotlinDeclarationProvider]. + * + * It works in IDE only, in standalone mode works [JvmClassFileBasedSymbolProvider]. + * + * Because it works over existing stubs, there is no need to keep huge protobuf in memory. + * At the same time, there is no need to guess sources for fir elements anymore, + * they are set during deserialization. + * + * Same as [JvmClassFileBasedSymbolProvider], resulting fir elements are already resolved. + */ +class JvmStubBasedFirDeserializedSymbolProvider( + session: FirSession, + moduleDataProvider: SingleModuleDataProvider, + private val kotlinScopeProvider: FirKotlinScopeProvider, + private val declarationProvider: KotlinDeclarationProvider +) : FirSymbolProvider(session) { + private val moduleData = moduleDataProvider.getModuleData(null) + private val packageSetWithTopLevelCallableDeclarations: Set by lazy(LazyThreadSafetyMode.PUBLICATION) { + declarationProvider.computePackageSetWithTopLevelCallableDeclarations() + } + + private val namesByPackageCache = LLFirKotlinSymbolProviderNameCache(session, declarationProvider) + + private val typeAliasCache: FirCache = + session.firCachesFactory.createCacheWithPostCompute( + createValue = { classId, _ -> findAndDeserializeTypeAlias(classId) }, + postCompute = { _, symbol, postProcessor -> + if (postProcessor != null && symbol != null) { + postProcessor.invoke(symbol) + } + } + ) + private val classCache: FirCache = + session.firCachesFactory.createCache( + createValue = { classId, context -> findAndDeserializeClass(classId, context) } + ) + + private val functionCache = session.firCachesFactory.createCache(::loadFunctionsByCallableId) + private val propertyCache = session.firCachesFactory.createCache(::loadPropertiesByCallableId) + + override fun computePackageSetWithTopLevelCallables(): Set { + return packageSetWithTopLevelCallableDeclarations + } + + override fun computeCallableNamesInPackage(packageFqName: FqName): Set? = + namesByPackageCache.getTopLevelCallableNamesInPackage(packageFqName) + + override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set? { + return namesByPackageCache.getTopLevelClassifierNamesInPackage(packageFqName) + } + + private fun findAndDeserializeTypeAlias(classId: ClassId): Pair { + val classLikeDeclaration = declarationProvider.getClassLikeDeclarationByClassId(classId)?.originalElement + if (classLikeDeclaration is KtTypeAlias) { + val symbol = FirTypeAliasSymbol(classId) + val postProcessor: DeserializedTypeAliasPostProcessor = { + val rootContext = StubBasedFirDeserializationContext.createRootContext( + moduleData, + StubBasedAnnotationDeserializer(session), + classId.packageFqName, + classId.relativeClassName, + classLikeDeclaration, + null, null, symbol + ) + rootContext.memberDeserializer.loadTypeAlias(classLikeDeclaration, symbol) + } + return symbol to postProcessor + } + return null to null + } + + private fun findAndDeserializeClass( + classId: ClassId, + parentContext: StubBasedFirDeserializationContext? = null + ): FirRegularClassSymbol? { + val classLikeDeclaration = declarationProvider.getClassLikeDeclarationByClassId(classId)?.originalElement ?: return null + val symbol = FirRegularClassSymbol(classId) + if (classLikeDeclaration is KtClassOrObject) { + deserializeClassToSymbol( + classId, + classLikeDeclaration, + symbol, + session, + moduleData, + StubBasedAnnotationDeserializer(session), + kotlinScopeProvider, + parentContext, + JvmFromStubDecompilerSource(JvmClassName.byClassId(classId)), + deserializeNestedClass = this::getClass, + ) + return symbol + } + return null + } + + private fun loadFunctionsByCallableId(callableId: CallableId): List { + val topLevelFunctions = declarationProvider.getTopLevelFunctions(callableId) + val origins = if (topLevelFunctions.size > 1) mutableSetOf() else null + return topLevelFunctions + .mapNotNull { function -> + val original = function.originalElement as? KtNamedFunction ?: return@mapNotNull null + if (origins != null && !origins.add(original)) return@mapNotNull null + val file = original.containingKtFile + val virtualFile = file.virtualFile + if (virtualFile.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION) return@mapNotNull null + if (file.packageFqName.asString() + .replace(".", "/") + "/" + virtualFile.nameWithoutExtension in KotlinBuiltins + ) return@mapNotNull null + val symbol = FirNamedFunctionSymbol(callableId) + val rootContext = + StubBasedFirDeserializationContext.createRootContext(session, moduleData, callableId, original, symbol) + rootContext.memberDeserializer.loadFunction(original, null, session, symbol).symbol + } + } + + private fun loadPropertiesByCallableId(callableId: CallableId): List { + val topLevelProperties = declarationProvider.getTopLevelProperties(callableId) + val origins = if (topLevelProperties.size > 1) mutableSetOf() else null + return topLevelProperties + .mapNotNull { property -> + val original = property.originalElement as? KtProperty ?: return@mapNotNull null + if (origins != null && !origins.add(original)) return@mapNotNull null + val symbol = FirPropertySymbol(callableId) + val rootContext = + StubBasedFirDeserializationContext.createRootContext(session, moduleData, callableId, original, symbol) + rootContext.memberDeserializer.loadProperty(original, null, symbol).symbol + } + } + + private fun getClass( + classId: ClassId, + parentContext: StubBasedFirDeserializationContext? = null + ): FirRegularClassSymbol? { + return classCache.getValue(classId, parentContext) + } + + private fun getTypeAlias(classId: ClassId): FirTypeAliasSymbol? { + if (!classId.relativeClassName.isOneSegmentFQN()) return null + return typeAliasCache.getValue(classId) + } + + @FirSymbolProviderInternals + override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) { + val callableId = CallableId(packageFqName, name) + destination += functionCache.getCallables(callableId) + destination += propertyCache.getCallables(callableId) + } + + private fun > FirCache, Nothing?>.getCallables(id: CallableId): List { + if (id.packageName.asString() !in packageSetWithTopLevelCallableDeclarations) return emptyList() + if (!namesByPackageCache.mayHaveTopLevelCallable(id.packageName, id.callableName)) return emptyList() + return getValue(id) + } + + @FirSymbolProviderInternals + override fun getTopLevelFunctionSymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { + destination += functionCache.getCallables(CallableId(packageFqName, name)) + } + + @FirSymbolProviderInternals + override fun getTopLevelPropertySymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { + destination += propertyCache.getCallables(CallableId(packageFqName, name)) + } + + override fun getPackage(fqName: FqName): FqName? { + return if (!namesByPackageCache.getTopLevelClassifierNamesInPackage(fqName) + .isNullOrEmpty() || packageSetWithTopLevelCallableDeclarations.contains(fqName.asString()) + ) { + fqName + } else null + } + + override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { + if (!namesByPackageCache.mayHaveTopLevelClassifier(classId, mayHaveFunctionClass = false)) return null + return getClass(classId) ?: getTypeAlias(classId) + } +} diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedAnnotationDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedAnnotationDeserializer.kt new file mode 100644 index 00000000000..1803e6ecb26 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedAnnotationDeserializer.kt @@ -0,0 +1,180 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.constant.* +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.KtRealPsiSourceElement +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.collectEnumEntries +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.expressions.builder.* +import org.jetbrains.kotlin.fir.references.builder.buildFromMissingDependenciesNamedReference +import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference +import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.symbols.lazyDeclarationResolver +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes +import org.jetbrains.kotlin.psi.stubs.impl.* +import org.jetbrains.kotlin.types.ConstantValueKind + +class StubBasedAnnotationDeserializer( + private val session: FirSession, +) { + //fun inheritAnnotationInfo(parent: StubBasedAnnotationDeserializer) {} + + fun loadAnnotations( + ktAnnotated: KtAnnotated, + useSiteTarget: AnnotationUseSiteTarget? = null + ): List { + val annotations = ktAnnotated.annotationEntries + if (annotations.isEmpty()) return emptyList() + return annotations.map { deserializeAnnotation(it, useSiteTarget) } + } + + private val constantCache = mutableMapOf() + + fun loadConstant(property: KtProperty, callableId: CallableId): FirExpression? { + if (!property.hasModifier(KtTokens.CONST_KEYWORD)) return null + constantCache[callableId]?.let { return it } + val propertyStub = property.stub as? KotlinPropertyStubImpl ?: return null + val constantValue = propertyStub.constantInitializer ?: return null + return resolveValue(property, constantValue) + } + + private fun deserializeAnnotation( + ktAnnotation: KtAnnotationEntry, + useSiteTarget: AnnotationUseSiteTarget? = null + ): FirAnnotation { + val userType = + ktAnnotation.getStubOrPsiChild(KtStubElementTypes.CONSTRUCTOR_CALLEE)?.getStubOrPsiChild(KtStubElementTypes.TYPE_REFERENCE) + ?.getStubOrPsiChild(KtStubElementTypes.USER_TYPE)!! + return deserializeAnnotation( + ktAnnotation, + userType.classId(), + (ktAnnotation.stub as? KotlinAnnotationEntryStubImpl)?.valueArguments, + useSiteTarget + ) + } + + private fun deserializeAnnotation( + ktAnnotation: PsiElement, + classId: ClassId, + valueArguments: Map>?, + useSiteTarget: AnnotationUseSiteTarget? = null + ): FirAnnotation { + return buildAnnotation { + source = KtRealPsiSourceElement(ktAnnotation) + annotationTypeRef = buildResolvedTypeRef { + type = classId.toLookupTag().constructClassType(emptyArray(), isNullable = false) + } + session.lazyDeclarationResolver.disableLazyResolveContractChecksInside { + this.argumentMapping = buildAnnotationArgumentMapping { + valueArguments?.forEach { (name, constantValue) -> + mapping[name] = resolveValue(ktAnnotation, constantValue) + } + } + } + useSiteTarget?.let { + this.useSiteTarget = it + } + } + } + + private fun resolveValue( + sourceElement: PsiElement, + value: ConstantValue<*> + ): FirExpression { + return when (value) { + is EnumValue -> sourceElement.toEnumEntryReferenceExpression(value.enumClassId, value.enumEntryName) + is KClassValue -> buildGetClassCall { + source = KtRealPsiSourceElement(sourceElement) + val lookupTag = (value.value as KClassValue.Value.NormalClass).classId.toLookupTag() + val referencedType = lookupTag.constructType(emptyArray(), isNullable = false) + val resolvedTypeRef = buildResolvedTypeRef { + type = StandardClassIds.KClass.constructClassLikeType(arrayOf(referencedType), false) + } + argumentList = buildUnaryArgumentList( + buildClassReferenceExpression { + classTypeRef = buildResolvedTypeRef { type = referencedType } + typeRef = resolvedTypeRef + } + ) + } + is ArrayValue -> buildArrayOfCall { + source = KtRealPsiSourceElement(sourceElement) + argumentList = buildArgumentList { + value.value.mapTo(arguments) { resolveValue(sourceElement, it) } + } +// typeRef = buildResolvedTypeRef { +// type = expectedArrayElementType.createArrayType() //todo +// } + } + is AnnotationValue -> { + deserializeAnnotation( + sourceElement, + (value.value.type as KotlinClassTypeBean).classId, + value.value.argumentsMapping + ) + } + is BooleanValue -> const(ConstantValueKind.Boolean, value.value, session.builtinTypes.booleanType, sourceElement) + is ByteValue -> const(ConstantValueKind.Byte, value.value, session.builtinTypes.byteType, sourceElement) + is CharValue -> const(ConstantValueKind.Char, value.value, session.builtinTypes.charType, sourceElement) + is ShortValue -> const(ConstantValueKind.Short, value.value, session.builtinTypes.shortType, sourceElement) + is LongValue -> const(ConstantValueKind.Long, value.value, session.builtinTypes.longType, sourceElement) + is FloatValue -> const(ConstantValueKind.Float, value.value, session.builtinTypes.floatType, sourceElement) + is DoubleValue -> const(ConstantValueKind.Double, value.value, session.builtinTypes.doubleType, sourceElement) + is UByteValue -> const(ConstantValueKind.UnsignedByte, value.value, session.builtinTypes.byteType, sourceElement) + is UShortValue -> const(ConstantValueKind.UnsignedShort, value.value, session.builtinTypes.shortType, sourceElement) + is UIntValue -> const(ConstantValueKind.UnsignedInt, value.value, session.builtinTypes.intType, sourceElement) + is ULongValue -> const(ConstantValueKind.UnsignedLong, value.value, session.builtinTypes.longType, sourceElement) + is IntValue -> const(ConstantValueKind.Int, value.value, session.builtinTypes.intType, sourceElement) + NullValue -> const(ConstantValueKind.Null, null, session.builtinTypes.nullableAnyType, sourceElement) + is StringValue -> const(ConstantValueKind.String, value.value, session.builtinTypes.stringType, sourceElement) + else -> error("Unexpected value $value") + } + } + + private fun const(kind: ConstantValueKind, value: T, typeRef: FirResolvedTypeRef, sourceElement: PsiElement): FirConstExpression { + return buildConstExpression( + KtRealPsiSourceElement(sourceElement), + kind, + value, + setType = true + ).apply { this.replaceTypeRef(typeRef) } + } + + private fun PsiElement.toEnumEntryReferenceExpression(classId: ClassId, entryName: Name): FirExpression { + return buildPropertyAccessExpression { + source = KtRealPsiSourceElement(this@toEnumEntryReferenceExpression) + val enumLookupTag = classId.toLookupTag() + val enumSymbol = enumLookupTag.toSymbol(session) + val firClass = enumSymbol?.fir as? FirRegularClass + val enumEntries = firClass?.collectEnumEntries() ?: emptyList() + val enumEntrySymbol = enumEntries.find { it.name == entryName } + calleeReference = enumEntrySymbol?.let { + buildResolvedNamedReference { + name = entryName + resolvedSymbol = it.symbol + } + } ?: buildFromMissingDependenciesNamedReference { + name = entryName + } + if (enumEntrySymbol != null) { + typeRef = enumEntrySymbol.returnTypeRef + } + } + } +} diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedClassDeserialization.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedClassDeserialization.kt new file mode 100644 index 00000000000..cf829439a37 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedClassDeserialization.kt @@ -0,0 +1,222 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.KtFakeSourceElement +import org.jetbrains.kotlin.KtRealPsiSourceElement +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.builder.createDataClassCopyFunction +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.builder.* +import org.jetbrains.kotlin.fir.declarations.comparators.FirMemberDeclarationComparator +import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl +import org.jetbrains.kotlin.fir.declarations.utils.* +import org.jetbrains.kotlin.fir.deserialization.* +import org.jetbrains.kotlin.fir.scopes.FirScopeProvider +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource + +internal val KtModifierListOwner.visibility: Visibility + get() = with(modifierList) { + when { + this == null -> Visibilities.Public + hasModifier(KtTokens.PRIVATE_KEYWORD) -> Visibilities.Private + hasModifier(KtTokens.PUBLIC_KEYWORD) -> Visibilities.Public + hasModifier(KtTokens.PROTECTED_KEYWORD) -> Visibilities.Protected + else -> if (hasModifier(KtTokens.INTERNAL_KEYWORD)) Visibilities.Internal else Visibilities.Public + } + } + +internal val KtDeclaration.modality: Modality + get() { + return when { + hasModifier(KtTokens.SEALED_KEYWORD) -> Modality.SEALED + hasModifier(KtTokens.ABSTRACT_KEYWORD) || this is KtClass && isInterface() -> Modality.ABSTRACT + hasModifier(KtTokens.OPEN_KEYWORD) -> Modality.OPEN + else -> Modality.FINAL + } + } + +fun deserializeClassToSymbol( + classId: ClassId, + classOrObject: KtClassOrObject, + symbol: FirRegularClassSymbol, + session: FirSession, + moduleData: FirModuleData, + defaultAnnotationDeserializer: StubBasedAnnotationDeserializer?, + scopeProvider: FirScopeProvider, + parentContext: StubBasedFirDeserializationContext? = null, + containerSource: DeserializedContainerSource? = null, + deserializeNestedClass: (ClassId, StubBasedFirDeserializationContext) -> FirRegularClassSymbol? +) { + val kind = when (classOrObject) { + is KtObjectDeclaration -> ClassKind.OBJECT + is KtClass -> when { + classOrObject.isInterface() -> ClassKind.INTERFACE + classOrObject.isEnum() -> ClassKind.ENUM_CLASS + classOrObject.isAnnotation() -> ClassKind.ANNOTATION_CLASS + else -> ClassKind.CLASS + } + else -> throw AssertionError("Unexpected class or object: ${classOrObject.text}") + } + val modality = classOrObject.modality + val visibility = classOrObject.visibility + val status = FirResolvedDeclarationStatusImpl( + visibility, + modality, + visibility.toEffectiveVisibility(parentContext?.outerClassSymbol, forClass = true) + ).apply { + isExpect = classOrObject.hasModifier(KtTokens.EXPECT_KEYWORD) + isActual = false + isInner = classOrObject.hasModifier(KtTokens.INNER_KEYWORD) + isCompanion = (classOrObject as? KtObjectDeclaration)?.isCompanion() == true + isData = classOrObject.hasModifier(KtTokens.DATA_KEYWORD) + isInline = classOrObject.hasModifier(KtTokens.INLINE_KEYWORD) || classOrObject.hasModifier(KtTokens.VALUE_KEYWORD) + isFun = classOrObject.hasModifier(KtTokens.FUN_KEYWORD) + isExternal = classOrObject.hasModifier(KtTokens.EXTERNAL_KEYWORD) + } + val annotationDeserializer = defaultAnnotationDeserializer ?: StubBasedAnnotationDeserializer(session) + val context = + parentContext?.childContext( + classOrObject, + classId.relativeClassName, + containerSource, + symbol, + annotationDeserializer, + status.isInner + ) ?: StubBasedFirDeserializationContext.createForClass( + classId, + classOrObject, + moduleData, + annotationDeserializer, + containerSource, + symbol + ) +// if (status.isCompanion) { +// parentContext?.let { +// context.annotationDeserializer.inheritAnnotationInfo(it.annotationDeserializer) +// } +// } + buildRegularClass { + source = KtRealPsiSourceElement(classOrObject) + this.moduleData = moduleData + this.origin = FirDeclarationOrigin.Library + name = classId.shortClassName + this.status = status + classKind = kind + this.scopeProvider = scopeProvider + this.symbol = symbol + + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + + typeParameters += context.typeDeserializer.ownTypeParameters.map { it.fir } + if (status.isInner) + typeParameters += parentContext?.allTypeParameters?.map { buildOuterClassTypeParameterRef { this.symbol = it } }.orEmpty() + + val typeDeserializer = context.typeDeserializer + val memberDeserializer = context.memberDeserializer + + val superTypeList = classOrObject.getSuperTypeList() + if (superTypeList != null) { + superTypeRefs.addAll(superTypeList.entries.map { + typeDeserializer.typeRef( + it.typeReference ?: error("Super entry doesn't have type reference $it") + ) + }) + } else if (StandardClassIds.Any != classId) { + superTypeRefs.add(session.builtinTypes.anyType) + } + + val firPrimaryConstructor = classOrObject.primaryConstructor?.let { + val constructor = memberDeserializer.loadConstructor(it, classOrObject, this) + addDeclaration(constructor) + constructor + } + classOrObject.body?.declarations?.forEach { declaration -> + when (declaration) { + is KtConstructor<*> -> addDeclaration(memberDeserializer.loadConstructor(declaration, classOrObject, this)) + is KtNamedFunction -> addDeclaration(memberDeserializer.loadFunction(declaration, symbol, session)) + is KtProperty -> addDeclaration(memberDeserializer.loadProperty(declaration, symbol)) + is KtEnumEntry -> addDeclaration(memberDeserializer.loadEnumEntry(declaration, symbol, classId)) + is KtClassOrObject -> { + val nestedClassId = + classId.createNestedClassId(Name.identifier(declaration.name ?: error("Class doesn't have name $declaration"))) + deserializeNestedClass(nestedClassId, context)?.fir?.let { addDeclaration(it) } + } + } + } + + if (classKind == ClassKind.ENUM_CLASS) { + generateValuesFunction( + moduleData, + classId.packageFqName, + classId.relativeClassName, + origin = FirDeclarationOrigin.Library + ) + generateValueOfFunction(moduleData, classId.packageFqName, classId.relativeClassName, origin = FirDeclarationOrigin.Library) + generateEntriesGetter(moduleData, classId.packageFqName, classId.relativeClassName, origin = FirDeclarationOrigin.Library) + } + + if (classOrObject.isData() && firPrimaryConstructor != null) { + val zippedParameters = + classOrObject.primaryConstructorParameters.filter { it.hasValOrVar() } zip declarations.filterIsInstance() + addDeclaration(createDataClassCopyFunction(classId, classOrObject, context.dispatchReceiver, zippedParameters, + createClassTypeRefWithSourceKind = { firPrimaryConstructor.returnTypeRef.copyWithNewSourceKind(it) }, + createParameterTypeRefWithSourceKind = { property, newKind -> + property.returnTypeRef.copyWithNewSourceKind(newKind) + }) { src, kind -> + KtFakeSourceElement(src as PsiElement, kind) + }) + } + + addCloneForArrayIfNeeded(classId, context.dispatchReceiver) + session.deserializedClassConfigurator?.run { + configure(classId) + } + + declarations.sortWith(object : Comparator { + override fun compare(a: FirDeclaration, b: FirDeclaration): Int { + // Reorder members based on their type and name only. + // See FE 1.0's [DeserializedMemberScope#addMembers]. + if (a is FirMemberDeclaration && b is FirMemberDeclaration) { + return FirMemberDeclarationComparator.TypeAndNameComparator.compare(a, b) + } + return 0 + } + }) + companionObjectSymbol = (declarations.firstOrNull { it is FirRegularClass && it.isCompanion } as FirRegularClass?)?.symbol + + contextReceivers.addAll(memberDeserializer.createContextReceiversForClass(classOrObject)) + }.apply { + //todo sealed inheritors + //if (modality == Modality.SEALED) { +// val inheritors = classOrObject.sealedSubclassFqNameList.map { nameIndex -> +// ClassId.fromString(nameResolver.getQualifiedClassName(nameIndex)) +// } +// setSealedClassInheritors(inheritors) + //} + + valueClassRepresentation = computeValueClassRepresentation(this, session) + + replaceAnnotations( + context.annotationDeserializer.loadAnnotations(classOrObject) + ) + + + sourceElement = containerSource + + replaceDeprecationsProvider(getDeprecationsProvider(session)) + + session.deserializedClassConfigurator?.run { + configure(classId) + } + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirContractDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirContractDeserializer.kt new file mode 100644 index 00000000000..c83c2bb3458 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirContractDeserializer.kt @@ -0,0 +1,150 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.contracts.description.* +import org.jetbrains.kotlin.fir.contracts.FirContractDescription +import org.jetbrains.kotlin.fir.contracts.builder.buildEffectDeclaration +import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription +import org.jetbrains.kotlin.fir.contracts.description.* +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.stubs.impl.* + +class StubBasedFirContractDeserializer( + private val simpleFunction: FirSimpleFunction, + private val typeDeserializer: StubBasedFirTypeDeserializer +) { + fun loadContract(function: KtNamedFunction): FirContractDescription? { + val functionStub = function.stub as? KotlinFunctionStubImpl ?: return null + val effects = functionStub.contract?.map { + it.accept(ContractDescriptionConvertingVisitor(), null) + } + + if (effects.isNullOrEmpty()) return null + return buildResolvedContractDescription { + this.effects += effects.map { description -> + buildEffectDeclaration { + effect = description as ConeEffectDeclaration + } + } + } + } + + inner class ContractDescriptionConvertingVisitor : + KtContractDescriptionVisitor() { + override fun visitCallsEffectDeclaration( + callsEffect: KtCallsEffectDeclaration, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeCallsEffectDeclaration( + callsEffect.valueParameterReference.accept( + this, + data + ) as KtValueParameterReference, callsEffect.kind + ) + } + + override fun visitReturnsEffectDeclaration( + returnsEffect: KtReturnsEffectDeclaration, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeReturnsEffectDeclaration( + returnsEffect.value.accept( + this, + data + ) as KtConstantReference + ) + } + + override fun visitConditionalEffectDeclaration( + conditionalEffect: KtConditionalEffectDeclaration, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeConditionalEffectDeclaration( + conditionalEffect.effect.accept(this, data) as KtEffectDeclaration, + conditionalEffect.condition.accept(this, data) as KtBooleanExpression + ) + } + + override fun visitLogicalBinaryOperationContractExpression( + binaryLogicExpression: KtBinaryLogicExpression, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeBinaryLogicExpression( + binaryLogicExpression.left.accept(this, data) as KtBooleanExpression, + binaryLogicExpression.right.accept(this, data) as KtBooleanExpression, + binaryLogicExpression.kind + ) + } + + override fun visitLogicalNot( + logicalNot: KtLogicalNot, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeLogicalNot(logicalNot.arg.accept(this, data) as KtBooleanExpression) + } + + override fun visitIsInstancePredicate( + isInstancePredicate: KtIsInstancePredicate, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeIsInstancePredicate( + isInstancePredicate.arg.accept(this, data) as KtValueParameterReference, + typeDeserializer.type(isInstancePredicate.type)!!, + isInstancePredicate.isNegated + ) + } + + override fun visitIsNullPredicate( + isNullPredicate: KtIsNullPredicate, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeIsNullPredicate( + isNullPredicate.arg.accept(this, data) as KtValueParameterReference, + isNullPredicate.isNegated + ) + } + + override fun visitValueParameterReference( + valueParameterReference: KtValueParameterReference, + data: Nothing? + ): ConeContractDescriptionElement { + val parameterIndex = valueParameterReference.parameterIndex + return ConeValueParameterReference(parameterIndex, getParameterName(parameterIndex)) + } + + override fun visitBooleanValueParameterReference( + booleanValueParameterReference: KtBooleanValueParameterReference, + data: Nothing? + ): ConeContractDescriptionElement { + return ConeBooleanValueParameterReference(booleanValueParameterReference.parameterIndex, + getParameterName(booleanValueParameterReference.parameterIndex)) + } + + private fun getParameterName(parameterIndex: Int): String { + return if (parameterIndex < 0) "this" else simpleFunction.valueParameters[parameterIndex].name.asString() + } + + override fun visitConstantDescriptor( + constantReference: KtConstantReference, + data: Nothing? + ): ConeContractDescriptionElement { + return when (constantReference) { + KotlinContractConstantValues.FALSE -> ConeContractConstantValues.FALSE + KotlinContractConstantValues.TRUE -> ConeContractConstantValues.TRUE + KotlinContractConstantValues.NULL -> ConeContractConstantValues.NULL + KotlinContractConstantValues.NOT_NULL -> ConeContractConstantValues.NOT_NULL + KotlinContractConstantValues.WILDCARD -> ConeContractConstantValues.WILDCARD + else -> { + error("Unexpected constant: $constantReference") + } + } + } + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt new file mode 100644 index 00000000000..2a8962e7fd3 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt @@ -0,0 +1,547 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.KtFakeSourceElement +import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.KtRealPsiSourceElement +import org.jetbrains.kotlin.descriptors.EffectiveVisibility +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.builder.* +import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl +import org.jetbrains.kotlin.fir.declarations.utils.sourceElement +import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionStub +import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.toEffectiveVisibility +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl +import org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource + +class StubBasedFirDeserializationContext( + val moduleData: FirModuleData, + val packageFqName: FqName, + val relativeClassName: FqName?, + val typeDeserializer: StubBasedFirTypeDeserializer, + val annotationDeserializer: StubBasedAnnotationDeserializer, + val containerSource: DeserializedContainerSource?, + val outerClassSymbol: FirRegularClassSymbol?, + val outerTypeParameters: List +) { + val session: FirSession = moduleData.session + + val allTypeParameters: List = + typeDeserializer.ownTypeParameters + outerTypeParameters + + fun childContext( + owner: KtTypeParameterListOwner, + relativeClassName: FqName? = this.relativeClassName, + containerSource: DeserializedContainerSource? = this.containerSource, + outerClassSymbol: FirRegularClassSymbol? = this.outerClassSymbol, + annotationDeserializer: StubBasedAnnotationDeserializer = this.annotationDeserializer, + capturesTypeParameters: Boolean = true, + containingDeclarationSymbol: FirBasedSymbol<*>? = this.outerClassSymbol + ): StubBasedFirDeserializationContext = StubBasedFirDeserializationContext( + moduleData, + packageFqName, + relativeClassName, + StubBasedFirTypeDeserializer( + moduleData, + annotationDeserializer, + typeDeserializer, + containingDeclarationSymbol, + owner + ), + annotationDeserializer, + containerSource, + outerClassSymbol, + if (capturesTypeParameters) allTypeParameters else emptyList() + ) + + val memberDeserializer: StubBasedFirMemberDeserializer = StubBasedFirMemberDeserializer(this) + val dispatchReceiver = relativeClassName?.let { ClassId(packageFqName, it, false).defaultType(allTypeParameters) } + + companion object { + + fun createForClass( + classId: ClassId, + classOrObject: KtClassOrObject, + moduleData: FirModuleData, + annotationDeserializer: StubBasedAnnotationDeserializer, + containerSource: DeserializedContainerSource?, + outerClassSymbol: FirRegularClassSymbol + ): StubBasedFirDeserializationContext = createRootContext( + moduleData, + annotationDeserializer, + classId.packageFqName, + classId.relativeClassName, + classOrObject, + containerSource, + outerClassSymbol, + outerClassSymbol + ) + + fun createRootContext( + moduleData: FirModuleData, + annotationDeserializer: StubBasedAnnotationDeserializer, + packageFqName: FqName, + relativeClassName: FqName?, + owner: KtTypeParameterListOwner, + containerSource: DeserializedContainerSource?, + outerClassSymbol: FirRegularClassSymbol?, + containingDeclarationSymbol: FirBasedSymbol<*>? + ): StubBasedFirDeserializationContext = StubBasedFirDeserializationContext( + moduleData, + packageFqName, + relativeClassName, + StubBasedFirTypeDeserializer( + moduleData, + annotationDeserializer, + null, + containingDeclarationSymbol, + owner + ), + annotationDeserializer, + containerSource, + outerClassSymbol, + emptyList() + ) + + fun createRootContext( + session: FirSession, + moduleData: FirModuleData, + callableId: CallableId, + parameterListOwner: KtTypeParameterListOwner, + symbol: FirBasedSymbol<*> + ): StubBasedFirDeserializationContext = createRootContext( + moduleData, + StubBasedAnnotationDeserializer(session), + callableId.packageName, + callableId.className, + parameterListOwner, + JvmFromStubDecompilerSource(callableId.packageName), + null, + symbol + ) + } +} + +class StubBasedFirMemberDeserializer(private val c: StubBasedFirDeserializationContext) { + + fun loadTypeAlias(typeAlias: KtTypeAlias, aliasSymbol: FirTypeAliasSymbol): FirTypeAlias { + val name = typeAlias.nameAsSafeName + val local = c.childContext(typeAlias, containingDeclarationSymbol = aliasSymbol) + return buildTypeAlias { + source = KtRealPsiSourceElement(typeAlias) + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + this.name = name + val visibility = typeAlias.visibility + status = FirResolvedDeclarationStatusImpl( + visibility, + Modality.FINAL, + visibility.toEffectiveVisibility(owner = null) + ).apply { + isExpect = typeAlias.hasModifier(KtTokens.EXPECT_KEYWORD) + isActual = false + } + + annotations += c.annotationDeserializer.loadAnnotations(typeAlias) + symbol = aliasSymbol + expandedTypeRef = typeAlias.getTypeReference()?.toTypeRef(local) ?: error("Type alias doesn't have type reference $typeAlias") + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + typeParameters += local.typeDeserializer.ownTypeParameters.map { it.fir } + }.apply { + sourceElement = c.containerSource + } + } + + private fun loadPropertyGetter( + getter: KtPropertyAccessor, + classSymbol: FirClassSymbol<*>?, + returnTypeRef: FirTypeRef, + propertySymbol: FirPropertySymbol, + ): FirPropertyAccessor { + val visibility = getter.visibility + val accessorModality = getter.modality + val effectiveVisibility = visibility.toEffectiveVisibility(classSymbol) + return buildPropertyAccessor { + source = KtRealPsiSourceElement(getter) + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + this.returnTypeRef = returnTypeRef + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + isGetter = true + status = FirResolvedDeclarationStatusImpl(visibility, accessorModality, effectiveVisibility).apply { + isInline = getter.hasModifier(KtTokens.INLINE_KEYWORD) + isExternal = getter.hasModifier(KtTokens.EXTERNAL_KEYWORD) + } + this.symbol = FirPropertyAccessorSymbol() + dispatchReceiverType = c.dispatchReceiver + this.propertySymbol = propertySymbol + }.apply { + replaceAnnotations( + c.annotationDeserializer.loadAnnotations( + getter, AnnotationUseSiteTarget.PROPERTY_GETTER + ) + ) + containingClassForStaticMemberAttr = c.dispatchReceiver?.lookupTag + } + } + + private fun loadPropertySetter( + setter: KtPropertyAccessor, + classSymbol: FirClassSymbol<*>?, + propertySymbol: FirPropertySymbol, + local: StubBasedFirDeserializationContext, + ): FirPropertyAccessor { + val visibility = setter.visibility + val accessorModality = setter.modality + val effectiveVisibility = visibility.toEffectiveVisibility(classSymbol) + return buildPropertyAccessor { + source = KtRealPsiSourceElement(setter) + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + this.returnTypeRef = FirImplicitUnitTypeRef(source) + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + isGetter = false + status = FirResolvedDeclarationStatusImpl(visibility, accessorModality, effectiveVisibility).apply { + isInline = setter.hasModifier(KtTokens.INLINE_KEYWORD) + isExternal = setter.hasModifier(KtTokens.EXTERNAL_KEYWORD) + } + this.symbol = FirPropertyAccessorSymbol() + dispatchReceiverType = c.dispatchReceiver + valueParameters += local.memberDeserializer.valueParameters( + setter.valueParameters, + symbol + ) + this.propertySymbol = propertySymbol + }.apply { + replaceAnnotations( + c.annotationDeserializer.loadAnnotations( + setter, AnnotationUseSiteTarget.PROPERTY_SETTER + ) + ) + containingClassForStaticMemberAttr = c.dispatchReceiver?.lookupTag + } + } + + fun loadProperty( + property: KtProperty, + classSymbol: FirClassSymbol<*>? = null, + existingSymbol: FirPropertySymbol? = null + ): FirProperty { + val callableName = property.nameAsSafeName + val callableId = CallableId(c.packageFqName, c.relativeClassName, callableName) + val symbol = existingSymbol ?: FirPropertySymbol(callableId) + val local = c.childContext(property, containingDeclarationSymbol = symbol) + + val returnTypeRef = property.typeReference?.toTypeRef(local) ?: error("Property doesn't have type reference, $property") + + val getter = property.getter + val receiverTypeReference = property.receiverTypeReference + val receiverAnnotations = if (getter != null && receiverTypeReference != null) { + c.annotationDeserializer.loadAnnotations(receiverTypeReference, AnnotationUseSiteTarget.PROPERTY_GETTER) + } else { + emptyList() + } + + val propertyModality = property.modality + + val isVar = property.isVar + return buildProperty { + source = KtRealPsiSourceElement(property) + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + this.returnTypeRef = returnTypeRef + receiverParameter = receiverTypeReference?.toTypeRef(local)?.let { receiverType -> + buildReceiverParameter { + typeRef = receiverType + annotations += receiverAnnotations + } + } + + name = callableName + this.isVar = isVar + this.symbol = symbol + dispatchReceiverType = c.dispatchReceiver + isLocal = false + val visibility = property.visibility + status = FirResolvedDeclarationStatusImpl(visibility, propertyModality, visibility.toEffectiveVisibility(classSymbol)).apply { + isExpect = property.hasExpectModifier() + isActual = false + isOverride = false + isConst = property.hasModifier(KtTokens.CONST_KEYWORD) + isLateInit = property.hasModifier(KtTokens.LATEINIT_KEYWORD) + isExternal = property.hasModifier(KtTokens.EXTERNAL_KEYWORD) + } + + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + typeParameters += local.typeDeserializer.ownTypeParameters.map { it.fir } + annotations += + c.annotationDeserializer.loadAnnotations(property, AnnotationUseSiteTarget.PROPERTY) + if (getter != null) { + this.getter = loadPropertyGetter( + getter, + classSymbol, + returnTypeRef, + symbol + ) + } + val setter = property.setter + if (setter != null) { + this.setter = loadPropertySetter( + setter, + classSymbol, + symbol, + local + ) + } + this.containerSource = c.containerSource + this.initializer = c.annotationDeserializer.loadConstant(property, symbol.callableId) + deprecationsProvider = annotations.getDeprecationsProviderFromAnnotations(c.session, fromJava = false) + + property.contextReceivers.mapNotNull { it.typeReference() }.mapTo(contextReceivers, ::loadContextReceiver) + } + } + + private fun loadContextReceiver(typeReference: KtTypeReference): FirContextReceiver { + val typeRef = typeReference.toTypeRef(c) + return buildContextReceiver { + source = KtRealPsiSourceElement(typeReference) + val type = typeRef.coneType + this.labelNameFromTypeRef = (type as? ConeLookupTagBasedType)?.lookupTag?.name + this.typeRef = typeRef + } + } + + internal fun createContextReceiversForClass(classOrObject: KtClassOrObject): List = + classOrObject.contextReceivers.mapNotNull { it.typeReference() }.map(::loadContextReceiver) + + fun loadFunction( + function: KtNamedFunction, + classSymbol: FirClassSymbol<*>? = null, + session: FirSession, + existingSymbol: FirNamedFunctionSymbol? = null + ): FirSimpleFunction { + val receiverAnnotations = if (function.receiverTypeReference != null) { + c.annotationDeserializer.loadAnnotations( + function + ) + } else { + emptyList() + } + + val callableName = function.nameAsSafeName + val callableId = CallableId(c.packageFqName, c.relativeClassName, callableName) + val symbol = existingSymbol ?: FirNamedFunctionSymbol(callableId) + val local = c.childContext(function, containingDeclarationSymbol = symbol) + + val simpleFunction = buildSimpleFunction { + moduleData = c.moduleData + origin = FirDeclarationOrigin.Library + source = KtRealPsiSourceElement(function) + returnTypeRef = function.typeReference?.toTypeRef(local) ?: session.builtinTypes.unitType + receiverParameter = function.receiverTypeReference?.toTypeRef(local)?.let { receiverType -> + buildReceiverParameter { + typeRef = receiverType + annotations += receiverAnnotations + } + } + + name = callableName + val visibility = function.visibility + status = FirResolvedDeclarationStatusImpl( + visibility, + function.modality, + visibility.toEffectiveVisibility(classSymbol) + ).apply { + isExpect = function.hasExpectModifier() + isActual = false + isOverride = false + isOperator = function.hasModifier(KtTokens.OPERATOR_KEYWORD) + isInfix = function.hasModifier(KtTokens.INFIX_KEYWORD) + isInline = function.hasModifier(KtTokens.INLINE_KEYWORD) + isTailRec = function.hasModifier(KtTokens.TAILREC_KEYWORD) + isExternal = function.hasModifier(KtTokens.EXTERNAL_KEYWORD) + isSuspend = function.hasModifier(KtTokens.SUSPEND_KEYWORD) + } + this.symbol = symbol + dispatchReceiverType = c.dispatchReceiver + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + typeParameters += local.typeDeserializer.ownTypeParameters.map { it.fir } + valueParameters += local.memberDeserializer.valueParameters( + function.valueParameters, + symbol + ) + annotations += + c.annotationDeserializer.loadAnnotations(function) + deprecationsProvider = annotations.getDeprecationsProviderFromAnnotations(c.session, fromJava = false) + this.containerSource = c.containerSource + + function.contextReceivers.mapNotNull { it.typeReference() }.mapTo(contextReceivers, ::loadContextReceiver) + } + if (function.mayHaveContract()) { + val resolvedDescription = StubBasedFirContractDeserializer(simpleFunction, local.typeDeserializer).loadContract(function) + if (resolvedDescription != null) { + simpleFunction.replaceContractDescription(resolvedDescription) + } + } + return simpleFunction + } + + fun loadConstructor( + constructor: KtConstructor<*>, + classOrObject: KtClassOrObject, + classBuilder: FirRegularClassBuilder + ): FirConstructor { + val relativeClassName = c.relativeClassName!! + val callableId = CallableId(c.packageFqName, relativeClassName, relativeClassName.shortName()) + val symbol = FirConstructorSymbol(callableId) + val local = c.childContext(constructor, containingDeclarationSymbol = symbol) + val isPrimary = constructor is KtPrimaryConstructor + + val typeParameters = classBuilder.typeParameters + + val delegatedSelfType = buildResolvedTypeRef { + type = ConeClassLikeTypeImpl( + classBuilder.symbol.toLookupTag(), + typeParameters.map { ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false) }.toTypedArray(), + false + ) + source = KtFakeSourceElement(classOrObject, KtFakeSourceElementKind.ClassSelfTypeRef) + } + + return if (isPrimary) { + FirPrimaryConstructorBuilder() + } else { + FirConstructorBuilder() + }.apply { + moduleData = c.moduleData + source = KtRealPsiSourceElement(constructor) + origin = FirDeclarationOrigin.Library + returnTypeRef = delegatedSelfType + val visibility = constructor.visibility + val isInner = classBuilder.status.isInner + status = FirResolvedDeclarationStatusImpl( + visibility, + Modality.FINAL, + visibility.toEffectiveVisibility(classBuilder.symbol) + ).apply { + isExpect = constructor.hasExpectModifier() || classOrObject.hasExpectModifier() + isActual = false + isOverride = false + this.isInner = isInner + } + this.symbol = symbol + dispatchReceiverType = + if (!isInner) null + else with(c) { + ClassId(packageFqName, relativeClassName.parent(), false).defaultType(outerTypeParameters) + } + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + this.typeParameters += + typeParameters.filterIsInstance() + .map { buildConstructedClassTypeParameterRef { this.symbol = it.symbol } } + valueParameters += local.memberDeserializer.valueParameters( + constructor.valueParameters, + symbol, + addDefaultValue = classBuilder.symbol.classId == StandardClassIds.Enum + ) + annotations += + c.annotationDeserializer.loadAnnotations(constructor) + containerSource = c.containerSource + deprecationsProvider = annotations.getDeprecationsProviderFromAnnotations(c.session, fromJava = false) + + contextReceivers.addAll(createContextReceiversForClass(classOrObject)) + }.build().apply { + containingClassForStaticMemberAttr = c.dispatchReceiver!!.lookupTag + } + } + + private fun valueParameters( + valueParameters: List, + functionSymbol: FirFunctionSymbol<*>, + addDefaultValue: Boolean = false + ): List { + return valueParameters.map { ktParameter -> + val name = ktParameter.nameAsSafeName + buildValueParameter { + source = KtRealPsiSourceElement(ktParameter) + moduleData = c.moduleData + this.containingFunctionSymbol = functionSymbol + origin = FirDeclarationOrigin.Library + returnTypeRef = + ktParameter.typeReference?.toTypeRef(c) ?: error("KtParameter $ktParameter doesn't have type, $functionSymbol") + isVararg = ktParameter.isVarArg + if (isVararg) { + returnTypeRef = returnTypeRef.withReplacedReturnType(returnTypeRef.coneType.createOutArrayType()) + } + this.name = name + symbol = FirValueParameterSymbol(name) + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + + defaultValue = if (ktParameter.hasDefaultValue()) { + buildExpressionStub() + } else null + if (addDefaultValue) { + defaultValue = buildExpressionStub() + } + isCrossinline = ktParameter.hasModifier(KtTokens.CROSSINLINE_KEYWORD) + isNoinline = ktParameter.hasModifier(KtTokens.NOINLINE_KEYWORD) + annotations += c.annotationDeserializer.loadAnnotations( + ktParameter + ) + } + }.toList() + } + + private fun KtTypeReference.toTypeRef(context: StubBasedFirDeserializationContext): FirTypeRef = + context.typeDeserializer.typeRef(this) + + fun loadEnumEntry( + declaration: KtEnumEntry, + symbol: FirRegularClassSymbol, + classId: ClassId + ): FirEnumEntry { + val enumEntryName = declaration.name ?: error("Enum entry doesn't provide name $declaration") + + val enumType = ConeClassLikeTypeImpl(symbol.toLookupTag(), emptyArray(), false) + val enumEntry = buildEnumEntry { + source = KtRealPsiSourceElement(declaration) + this.moduleData = c.moduleData + this.origin = FirDeclarationOrigin.Library + returnTypeRef = buildResolvedTypeRef { type = enumType } + name = Name.identifier(enumEntryName) + this.symbol = FirEnumEntrySymbol(CallableId(classId, name)) + this.status = FirResolvedDeclarationStatusImpl( + Visibilities.Public, + Modality.FINAL, + EffectiveVisibility.Public + ).apply { + isStatic = true + } + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + }.apply { + containingClassForStaticMemberAttr = c.dispatchReceiver!!.lookupTag + } + return enumEntry + } +} diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirTypeDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirTypeDeserializer.kt new file mode 100644 index 00000000000..97384b07e43 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirTypeDeserializer.kt @@ -0,0 +1,283 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization + +import org.jetbrains.kotlin.KtRealPsiSourceElement +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.computeTypeAttributes +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner +import org.jetbrains.kotlin.fir.declarations.builder.FirTypeParameterBuilder +import org.jetbrains.kotlin.fir.declarations.utils.addDefaultBoundIfNecessary +import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic +import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag +import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag +import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier +import org.jetbrains.kotlin.psi.psiUtil.unwrapNullability +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes +import org.jetbrains.kotlin.psi.stubs.impl.* +import org.jetbrains.kotlin.types.Variance + +class StubBasedFirTypeDeserializer( + private val moduleData: FirModuleData, + private val annotationDeserializer: StubBasedAnnotationDeserializer, + private val parent: StubBasedFirTypeDeserializer?, + private val containingSymbol: FirBasedSymbol<*>?, + owner: KtTypeParameterListOwner +) { + private val typeParametersByName: Map + + val ownTypeParameters: List + get() = typeParametersByName.values.toList() + + init { + val typeParameters = owner.typeParameters + if (typeParameters.isNotEmpty()) { + typeParametersByName = mutableMapOf() + val builders = mutableListOf() + for (typeParameter in typeParameters) { + val name = typeParameter.nameAsSafeName + val symbol = FirTypeParameterSymbol().also { + typeParametersByName[name.asString()] = it + } + builders += FirTypeParameterBuilder().apply { + source = KtRealPsiSourceElement(typeParameter) + moduleData = this@StubBasedFirTypeDeserializer.moduleData + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + origin = FirDeclarationOrigin.Library + this.name = name + this.symbol = symbol + this.containingDeclarationSymbol = containingSymbol ?: error("Top-level type parameter ???") + variance = typeParameter.variance + isReified = typeParameter.hasModifier(KtTokens.REIFIED_KEYWORD) + annotations += annotationDeserializer.loadAnnotations(typeParameter) + } + } + + for ((index, typeParameter) in typeParameters.withIndex()) { + val builder = builders[index] + builder.apply { + typeParameter.extendsBound?.let { bounds.add(typeRef(it)) } + owner.typeConstraints + .filter { it.subjectTypeParameterName?.getReferencedNameAsName() == typeParameter.nameAsName } + .forEach { typeConstraint -> typeConstraint.boundTypeReference?.let { bounds += typeRef(it) } } + addDefaultBoundIfNecessary() + }.build() + } + } else { + typeParametersByName = emptyMap() + } + } + + private fun computeClassifier(classId: ClassId?): ConeClassLikeLookupTag? { + return classId?.toLookupTag() + } + + fun typeRef(typeReference: KtTypeReference): FirTypeRef { + return buildResolvedTypeRef { + source = KtRealPsiSourceElement(typeReference) + annotations += annotationDeserializer.loadAnnotations(typeReference) + type = type(typeReference, annotations.computeTypeAttributes(moduleData.session, shouldExpandTypeAliases = false)) + } + } + + private fun attributesFromAnnotations(typeReference: KtTypeReference): ConeAttributes = + annotationDeserializer.loadAnnotations(typeReference).computeTypeAttributes(moduleData.session, shouldExpandTypeAliases = false) + + fun type(typeReference: KtTypeReference): ConeKotlinType { + return type(typeReference, attributesFromAnnotations(typeReference)) + } + + fun type(type: KotlinTypeBean): ConeKotlinType? { + when (type) { + is KotlinTypeParameterTypeBean -> { + val lookupTag = + typeParametersByName[type.typeParameterName]?.toLookupTag() ?: parent?.typeParameterSymbol(type.typeParameterName) + ?: return null + return ConeTypeParameterTypeImpl(lookupTag, isNullable = type.nullable).let { + if (type.definitelyNotNull) + ConeDefinitelyNotNullType.create(it, moduleData.session.typeContext, avoidComprehensiveCheck = true) ?: it + else + it + } + } + is KotlinClassTypeBean -> { + val projections = type.arguments.map { typeArgumentBean -> + val kind = typeArgumentBean.projectionKind + if (kind == KtProjectionKind.STAR) { + return@map ConeStarProjection + } + val argBean = typeArgumentBean.type!! + val lowerBound = type(argBean) ?: error("Broken type argument ${typeArgumentBean.type}") + typeArgument(lowerBound, kind) + } + return ConeClassLikeTypeImpl( + type.classId.toLookupTag(), + projections.toTypedArray(), + isNullable = type.nullable, + ConeAttributes.Empty + ) + } + is KotlinFlexibleTypeBean -> { + val lowerBound = type(type.lowerBound) + val upperBound = type(type.upperBound) + return ConeFlexibleType( + lowerBound as? ConeSimpleKotlinType ?: error("Unexpected lower bound $lowerBound"), + upperBound as? ConeSimpleKotlinType ?: error("Unexpected upper bound $upperBound") + ) + } + } + } + + private fun type(typeReference: KtTypeReference, attributes: ConeAttributes): ConeKotlinType { + val upperBoundType = ((typeReference.typeElement as? KtUserType)?.stub as? KotlinUserTypeStubImpl)?.upperBound + if (upperBoundType != null) { + val lowerBound = simpleType(typeReference, attributes) + val upperBound = type(upperBoundType) + + val isDynamic = lowerBound == moduleData.session.builtinTypes.nothingType.coneType && + upperBound == moduleData.session.builtinTypes.nullableAnyType.coneType + + return if (isDynamic) { + ConeDynamicType.create(moduleData.session) + } else { + ConeFlexibleType(lowerBound!!, upperBound as ConeSimpleKotlinType) + } + } + + return simpleType(typeReference, attributes) ?: ConeErrorType(ConeSimpleDiagnostic("?!id:0", DiagnosticKind.DeserializationError)) + } + + private fun typeParameterSymbol(typeParameterName: String): ConeTypeParameterLookupTag? = + typeParametersByName[typeParameterName]?.toLookupTag() ?: parent?.typeParameterSymbol(typeParameterName) + + fun FirClassLikeSymbol<*>.typeParameters(): List = + (fir as? FirTypeParameterRefsOwner)?.typeParameters?.map { it.symbol }.orEmpty() + + private fun simpleType(typeReference: KtTypeReference, attributes: ConeAttributes): ConeSimpleKotlinType? { + val constructor = typeSymbol(typeReference) ?: return null + val isNullable = typeReference.typeElement is KtNullableType + if (constructor is ConeTypeParameterLookupTag) { + return ConeTypeParameterTypeImpl(constructor, isNullable = isNullable).let { + if (typeReference.typeElement?.unwrapNullability() is KtIntersectionType) { + ConeDefinitelyNotNullType.create(it, moduleData.session.typeContext, avoidComprehensiveCheck = true) ?: it + } else it + } + } + if (constructor !is ConeClassLikeLookupTag) return null + + val typeElement = typeReference.typeElement?.unwrapNullability() + val arguments = when (typeElement) { + is KtUserType -> typeElement.typeArguments.map { typeArgument(it) }.toTypedArray() + is KtFunctionType -> buildList { + typeElement.receiver?.let { add(type(it.typeReference).toTypeProjection(Variance.INVARIANT)) } + addAll(typeElement.parameters.map { type(it.typeReference!!).toTypeProjection(Variance.INVARIANT) }) + add(type(typeElement.returnTypeReference!!).toTypeProjection(Variance.INVARIANT)) + }.toTypedArray() + else -> error("not supported $typeElement") + } + + return ConeClassLikeTypeImpl( + constructor, + arguments, + isNullable = isNullable, + if (typeElement is KtFunctionType && typeElement.receiver != null) ConeAttributes.WithExtensionFunctionType else attributes + ) + } + + private fun KtElementImplStub<*>.getAllModifierLists(): Array = + getStubOrPsiChildren(KtStubElementTypes.MODIFIER_LIST, KtStubElementTypes.MODIFIER_LIST.arrayFactory) + + private fun typeSymbol(typeReference: KtTypeReference): ConeClassifierLookupTag? { + val typeElement = typeReference.typeElement?.unwrapNullability() + if (typeElement is KtFunctionType) { + val arity = (if (typeElement.receiver != null) 1 else 0) + typeElement.parameters.size + val isSuspend = typeReference.getAllModifierLists().any { it.hasSuspendModifier() } + val functionClassId = if (isSuspend) StandardNames.getSuspendFunctionClassId(arity) else StandardNames.getFunctionClassId(arity) + return computeClassifier(functionClassId) + } + if (typeElement is KtIntersectionType) { + val leftTypeRef = typeElement.getLeftTypeRef() ?: return null + //T&Any + return typeSymbol(leftTypeRef) + } + val type = typeElement as KtUserType + val referencedName = type.referencedName + return typeParameterSymbol(referencedName!!) ?: computeClassifier(type.classId()) + } + + + private fun typeArgument(projection: KtTypeProjection): ConeTypeProjection { + if (projection.projectionKind == KtProjectionKind.STAR) { + return ConeStarProjection + } + + val type = type(projection.typeReference!!) + return typeArgument(type, projection.projectionKind) + } + + private fun typeArgument( + type: ConeKotlinType, + projectionKind: KtProjectionKind + ): ConeTypeProjection { + val variance = when (projectionKind) { + KtProjectionKind.IN -> Variance.IN_VARIANCE + KtProjectionKind.OUT -> Variance.OUT_VARIANCE + KtProjectionKind.NONE -> Variance.INVARIANT + KtProjectionKind.STAR -> throw AssertionError("* should not be here") + } + return type.toTypeProjection(variance) + } +} + +/** + * Retrieves classId from [KtUserType] for compiled code only. + * + * It relies on [org.jetbrains.kotlin.psi.stubs.impl.KotlinNameReferenceExpressionStubImpl.isClassRef], + * which is set during cls analysis only. + */ +internal fun KtUserType.classId(): ClassId { + val packageFragments = mutableListOf() + val classFragments = mutableListOf() + + fun collectFragments(type: KtUserType) { + val userType = type.getStubOrPsiChild(KtStubElementTypes.USER_TYPE) + if (userType != null) { + collectFragments(userType) + } + val referenceExpression = type.referenceExpression as? KtNameReferenceExpression + if (referenceExpression != null) { + val referencedName = referenceExpression.getReferencedName() + val stub = referenceExpression.stub + if (stub is KotlinNameReferenceExpressionStubImpl && stub.isClassRef) { + classFragments.add(referencedName) + } else { + packageFragments.add(referencedName) + } + } + } + collectFragments(this) + return ClassId( + FqName.fromSegments(packageFragments), + FqName.fromSegments(classFragments), + false + ) +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AnalysisApiFirTestServiceRegistrar.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AnalysisApiFirTestServiceRegistrar.kt index e3199f9b6e6..0ceb7f2c0cd 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AnalysisApiFirTestServiceRegistrar.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AnalysisApiFirTestServiceRegistrar.kt @@ -16,10 +16,12 @@ import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirGlobalResolveComponents import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirResolveSessionService import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory +import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.JvmFirDeserializedSymbolProviderFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirBuiltinsSessionFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.services.NoOpKtCompilerPluginsProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLFirSealedClassInheritorsProcessorFactoryForTests import org.jetbrains.kotlin.analysis.low.level.api.fir.services.PackagePartProviderTestImpl +import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.JvmStubBasedDeserializedSymbolProviderFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionCache import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionConfigurator import org.jetbrains.kotlin.analysis.project.structure.KtCompilerPluginsProvider @@ -59,6 +61,7 @@ object AnalysisApiFirTestServiceRegistrar : AnalysisApiTestServiceRegistrar() { registerService(KtCompilerPluginsProvider::class.java, NoOpKtCompilerPluginsProvider) registerService(ReadWriteAccessChecker::class.java, ReadWriteAccessCheckerFirImpl()) registerService(KotlinReferenceProviderContributor::class.java, KotlinFirReferenceContributor::class.java) + registerService(JvmFirDeserializedSymbolProviderFactory::class.java, JvmStubBasedDeserializedSymbolProviderFactory::class.java) } } diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/FirLowLevelCompilerBasedTestConfigurator.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/FirLowLevelCompilerBasedTestConfigurator.kt index 8790cf9eb15..f092f9b6f00 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/FirLowLevelCompilerBasedTestConfigurator.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/FirLowLevelCompilerBasedTestConfigurator.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder import org.jetbrains.kotlin.test.services.TestModuleStructure import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleWithFiles import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule @@ -33,6 +34,7 @@ object FirLowLevelCompilerBasedTestConfigurator : AnalysisApiTestConfigurator() override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFirTestServiceRegistrar, ) diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirSourceLikeTestConfigurator.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirSourceLikeTestConfigurator.kt index 74c7dbca397..f82435a5556 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirSourceLikeTestConfigurator.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirSourceLikeTestConfigurator.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar +import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.SealedClassesInheritorsCaclulatorPreAnalysisHandler import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.AnalysisApiFirTestServiceRegistrar @@ -35,6 +36,7 @@ abstract class AnalysisApiFirSourceLikeTestConfigurator(override val analyseInDe override val serviceRegistrars: List = listOf( AnalysisApiBaseTestServiceRegistrar, + AnalysisApiDecompiledCodeTestServiceRegistrar, AnalysisApiFirTestServiceRegistrar, ) diff --git a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/ClassDeserialization.kt b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/ClassDeserialization.kt index 8f9820026c2..f55f03f8cca 100644 --- a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/ClassDeserialization.kt +++ b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/ClassDeserialization.kt @@ -270,7 +270,7 @@ private fun FirRegularClassBuilder.addSerializableIfNeeded(classId: ClassId) { } } -private fun FirRegularClassBuilder.addCloneForArrayIfNeeded(classId: ClassId, dispatchReceiver: ConeClassLikeType?) { +fun FirRegularClassBuilder.addCloneForArrayIfNeeded(classId: ClassId, dispatchReceiver: ConeClassLikeType?) { if (classId.packageFqName != StandardClassIds.BASE_KOTLIN_PACKAGE) return if (classId.shortClassName !in ARRAY_CLASSES) return superTypeRefs += buildResolvedTypeRef { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt index ecb09add760..8f7a78362f4 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.name.Name // Use it in application sessions for loading classes from Java files listed on the command line. // For library and incremental compilation sessions use `KotlinDeserializedJvmSymbolsProvider` // in order to load Kotlin classes as well. +//Also used in IDE for loading java classes separately from stub based kotlin classes class JavaSymbolProvider( session: FirSession, private val javaFacade: FirJavaFacade, diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt index 71168c94264..378641dc354 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt @@ -1056,6 +1056,7 @@ fun FirRegularClassBuilder.createDataClassCopyFunction( status = FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL) symbol = FirNamedFunctionSymbol(CallableId(classId.packageFqName, classId.relativeClassName, StandardNames.DATA_CLASS_COPY)) dispatchReceiverType = dispatchReceiver + resolvePhase = this@createDataClassCopyFunction.resolvePhase for ((ktParameter, firProperty) in zippedParameters) { val propertyName = firProperty.name val parameterSource = toFirSource(ktParameter, KtFakeSourceElementKind.DataClassGeneratedMembers) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/EnumClassUtils.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/EnumClassUtils.kt index 022cc83799c..509dc73c318 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/EnumClassUtils.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/EnumClassUtils.kt @@ -160,7 +160,7 @@ fun FirRegularClassBuilder.generateEntriesGetter( getter = FirDefaultPropertyGetter( sourceElement?.fakeElement(KtFakeSourceElementKind.EnumGeneratedDeclaration), moduleData, origin, returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.EnumGeneratedDeclaration), - Visibilities.Public, symbol + Visibilities.Public, symbol, resolvePhase = this@generateEntriesGetter.resolvePhase ).apply { this.status = createStatus(this@generateEntriesGetter.status).apply { isStatic = true diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtNameReferenceExpressionElementType.java b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtNameReferenceExpressionElementType.java index 3b82c005eed..985792e7496 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtNameReferenceExpressionElementType.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtNameReferenceExpressionElementType.java @@ -41,12 +41,15 @@ public class KtNameReferenceExpressionElementType extends KtStubElementType parent, + @NotNull StringRef referencedName, + boolean myClassRef + ) { + super(parent, KtStubElementTypes.REFERENCE_EXPRESSION); + this.referencedName = referencedName; + this.myClassRef = myClassRef; + } + + public boolean isClassRef() { + return myClassRef; } @NotNull