Analysis API FIR: introduce library resolve tests

This commit is contained in:
Ilya Kirillov
2022-01-25 16:46:43 +01:00
parent 96e1f1e7d4
commit caddbda01b
22 changed files with 1784 additions and 128 deletions
@@ -10,9 +10,9 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleModuleTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.utils.libraries.CompilerExecutor
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.light.classes.symbol.base.service.NullabilityAnnotationSourceProvider
import org.jetbrains.kotlin.light.classes.symbol.decompiled.service.CompilerExecutor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability
@@ -1,59 +0,0 @@
/*
* 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.light.classes.symbol.decompiled.service
import org.jetbrains.kotlin.test.MockLibraryUtil
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.model.TestModule
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.div
import kotlin.io.path.exists
import kotlin.io.path.notExists
object CompilerExecutor {
fun compileLibrary(sourcesPath: Path, options: List<String>, compilationErrorExpected: Boolean): Path? {
val library = sourcesPath / "library.jar"
val sourceFiles = sourcesPath.toFile().walkBottomUp()
val commands = buildList {
sourceFiles.mapTo(this) { it.absolutePath }
addAll(options)
add("-d")
add(library.absolutePathString())
}
try {
MockLibraryUtil.runJvmCompiler(commands)
} catch (e: Throwable) {
if (!compilationErrorExpected) {
throw IllegalStateException("Unexpected compilation error while compiling library", e)
}
}
if (library.exists() && compilationErrorExpected) {
error("Compilation error expected but, code was compiled successfully")
}
return library.takeIf(Path::exists)
}
fun parseCompilerOptionsFromTestdata(module: TestModule): List<String> = buildList {
module.directives[LanguageSettingsDirectives.API_VERSION].firstOrNull()?.let { apiVersion ->
addAll(listOf("-api-version", apiVersion.versionString))
}
module.directives[JvmEnvironmentConfigurationDirectives.JVM_TARGET].firstOrNull()?.let { jvmTarget ->
addAll(listOf("-jvm-target", jvmTarget.description))
}
addAll(module.directives[Directives.COMPILER_ARGUMENTS])
}
object Directives : SimpleDirectivesContainer() {
val COMPILER_ARGUMENTS by stringDirective("List of additional compiler arguments")
val COMPILATION_ERRORS by directive("Is compilation errors expected in the file")
}
}
@@ -1,79 +0,0 @@
/*
* 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.light.classes.symbol.decompiled.service
import com.intellij.mock.MockProject
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.TestKtLibraryModule
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.registerTestServices
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.util.KtTestUtil
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.createFile
import kotlin.io.path.div
import kotlin.io.path.writeText
class LibraryModuleRegistrarPreAnalysisHandler(
testServices: TestServices
) : PreAnalysisHandler(testServices) {
private val moduleInfoProvider = testServices.projectModuleProvider
override fun preprocessModuleStructure(moduleStructure: TestModuleStructure) {
val testModule = moduleStructure.modules.single()
val project = testServices.compilerConfigurationProvider.getProject(testModule)
val decompiledKtFiles = getDecompiledVirtualFilesFromLibrary(testModule, project)
moduleInfoProvider.registerModuleInfo(testModule, TestKtLibraryModule(project, testModule, decompiledKtFiles, testServices))
(project as MockProject).registerTestServices(testModule, decompiledKtFiles, testServices)
}
private fun getDecompiledVirtualFilesFromLibrary(module: TestModule, project: Project): List<KtClsFile> {
val tmpDir = KtTestUtil.tmpDir("testSourcesToCompile").toPath()
for (testFile in module.files) {
val text = testServices.sourceFileProvider.getContentOfSourceFile(testFile)
val tmpSourceFile = (tmpDir / testFile.name).createFile()
tmpSourceFile.writeText(text)
}
val library = CompilerExecutor.compileLibrary(
tmpDir,
CompilerExecutor.parseCompilerOptionsFromTestdata(module),
compilationErrorExpected = CompilerExecutor.Directives.COMPILATION_ERRORS in module.directives
) ?: return emptyList()
val virtualFiles = getAllVirtualFilesFromLibrary(library)
return virtualFiles.mapNotNull { virtualFile ->
PsiManager.getInstance(project).findFile(virtualFile) as? KtClsFile
}
}
private fun getAllVirtualFilesFromLibrary(library: Path): Collection<VirtualFile> {
val jarFileSystem = CoreJarFileSystem()
val root = jarFileSystem.refreshAndFindFileByPath(library.absolutePathString() + "!/")!!
val files = mutableSetOf<VirtualFile>()
VfsUtilCore.iterateChildrenRecursively(
root,
/*filter=*/{ true },
/*iterator=*/{ virtualFile ->
if (!virtualFile.isDirectory) {
files.add(virtualFile)
}
true
}
)
return files
}
}
@@ -0,0 +1,39 @@
/*
* 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.light.classes.symbol.decompiled.service
import com.intellij.mock.MockApplication
import com.intellij.mock.MockProject
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.analysis.api.fir.utils.libraries.binary.LibraryFrontendApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.impl.base.test.utils.libraries.CompiledLibraryProvider
import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.services.ServiceRegistrationData
internal object SymbolLightClassesForLibraryFrontendApiTestConfiguratorService : FrontendApiTestConfiguratorService {
override fun TestConfigurationBuilder.configureTest(disposable: Disposable) {
with(LibraryFrontendApiTestConfiguratorService) {
configureTest(disposable)
}
useAdditionalServices(ServiceRegistrationData(CompiledLibraryProvider::class, ::CompiledLibraryProvider))
}
override fun registerProjectServices(project: MockProject) {
LibraryFrontendApiTestConfiguratorService.registerProjectServices(project)
project.registerService(ClsJavaStubByVirtualFileCache::class.java)
}
override fun registerApplicationServices(application: MockApplication) {
LibraryFrontendApiTestConfiguratorService.registerApplicationServices(application)
}
override fun doOutOfBlockModification(file: KtFile) {
LibraryFrontendApiTestConfiguratorService.doOutOfBlockModification(file)
}
}
@@ -1,55 +0,0 @@
/*
* 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.light.classes.symbol.decompiled.service
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.mock.MockApplication
import com.intellij.mock.MockProject
import com.intellij.openapi.Disposable
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.api.fir.FirFrontendApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache
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.psi.KtFile
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
object SymbolLightClassesForLibraryFrontendApiTestConfiguratorService : FrontendApiTestConfiguratorService {
override fun TestConfigurationBuilder.configureTest(disposable: Disposable) {
usePreAnalysisHandlers(::LibraryModuleRegistrarPreAnalysisHandler)
}
override fun registerProjectServices(project: MockProject) {
project.registerService(ClsJavaStubByVirtualFileCache::class.java)
}
override fun registerApplicationServices(application: MockApplication) {
FirFrontendApiTestConfiguratorService.registerApplicationServices(application)
if (application.getServiceIfCreated(ClsKotlinBinaryClassCache::class.java) != null) return
application.registerService(ClsKotlinBinaryClassCache::class.java)
application.registerService(FileAttributeService::class.java, DummyFileAttributeService)
FileTypeFileViewProviders.INSTANCE.addExplicitExtension(
JavaClassFileType.INSTANCE,
ClassFileViewProviderFactory()
)
@Suppress("DEPRECATION")
ClassFileDecompilers.getInstance().EP_NAME.point.registerExtension(KotlinClassFileDecompiler(), LoadingOrder.FIRST)
@Suppress("DEPRECATION")
ClassFileDecompilers.getInstance().EP_NAME.point.registerExtension(KotlinBuiltInDecompiler(), LoadingOrder.FIRST)
}
override fun doOutOfBlockModification(file: KtFile) {
FirFrontendApiTestConfiguratorService.doOutOfBlockModification(file)
}
}