[FIR] Support creation of IR of common + platform sources in FirAnalyzerFacade

This commit is contained in:
Dmitriy Novozhilov
2021-06-03 16:28:39 +03:00
committed by teamcityserver
parent df11ccf755
commit 37c096cb10
16 changed files with 173 additions and 32 deletions
@@ -13,6 +13,7 @@ import com.intellij.openapi.vfs.VirtualFileSystem
import com.intellij.psi.PsiElementFinder
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.ProjectScope
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
@@ -30,16 +31,17 @@ import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.CodegenFactory
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.fir.DependencyListForCliModule
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.FirAnalyzerFacade
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
import org.jetbrains.kotlin.fir.backend.Fir2IrResult
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendClassResolver
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendExtension
import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.session.FirSessionFactory
import org.jetbrains.kotlin.fir.session.FirSessionFactory.createSessionWithDependencies
import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackagePartProvider
@@ -48,10 +50,16 @@ import org.jetbrains.kotlin.modules.Module
import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
import java.io.File
import kotlin.collections.set
@@ -148,11 +156,13 @@ object FirKotlinToJvmBytecodeCompiler {
}
private fun CompilationContext.runFrontend(ktFiles: List<KtFile>): FirAnalyzerFacade? {
@Suppress("NAME_SHADOWING")
var ktFiles = ktFiles
val syntaxErrors = ktFiles.fold(false) { errorsFound, ktFile ->
AnalyzerWithCompilerReport.reportSyntaxErrors(ktFile, environment.messageCollector).isHasErrors or errorsFound
}
val sourceScope = GlobalSearchScope.filesWithoutLibrariesScope(project, ktFiles.map { it.virtualFile })
var sourceScope = GlobalSearchScope.filesWithoutLibrariesScope(project, ktFiles.map { it.virtualFile })
.uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(project))
var librariesScope = ProjectScope.getLibrariesScope(project)
@@ -164,35 +174,78 @@ object FirKotlinToJvmBytecodeCompiler {
}
val languageVersionSettings = moduleConfiguration.languageVersionSettings
val session = createSessionWithDependencies(
Name.identifier(module.getModuleName()),
val commonKtFiles = ktFiles.filter { it.isCommonSource == true }
val sessionProvider = FirProjectSessionProvider()
fun createSession(
name: String,
platform: TargetPlatform,
analyzerServices: PlatformDependentAnalyzerServices,
sourceScope: GlobalSearchScope,
dependenciesConfigurator: DependencyListForCliModule.Builder.() -> Unit = {}
): FirSession {
return createSessionWithDependencies(
Name.identifier(name),
platform,
analyzerServices,
externalSessionProvider = sessionProvider,
project,
languageVersionSettings,
sourceScope,
librariesScope,
lookupTracker = environment.configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER),
providerAndScopeForIncrementalCompilation,
getPackagePartProvider = { environment.createPackagePartProvider(it) },
dependenciesConfigurator = {
dependencies(moduleConfiguration.jvmClasspathRoots.map { it.toPath() })
dependencies(moduleConfiguration.jvmModularRoots.map { it.toPath() })
friendDependencies(moduleConfiguration[JVMConfigurationKeys.FRIEND_PATHS] ?: emptyList())
dependenciesConfigurator()
},
sessionConfigurator = {
if (extendedAnalysisMode) {
registerExtendedCommonCheckers()
}
}
)
}
val commonSession = runIf(
languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects) && commonKtFiles.isNotEmpty()
) {
val commonSourcesScope = GlobalSearchScope.filesWithoutLibrariesScope(project, commonKtFiles.map { it.virtualFile })
sourceScope = sourceScope.intersectWith(GlobalSearchScope.notScope(commonSourcesScope))
ktFiles = ktFiles.filterNot { it.isCommonSource == true }
createSession(
"${module.getModuleName()}-common",
CommonPlatforms.defaultCommonPlatform,
CommonPlatformAnalyzerServices,
commonSourcesScope
)
}
val session = createSession(
module.getModuleName(),
JvmPlatforms.unspecifiedJvmPlatform,
JvmPlatformAnalyzerServices,
externalSessionProvider = null,
project,
languageVersionSettings,
sourceScope,
librariesScope,
lookupTracker = environment.configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER),
providerAndScopeForIncrementalCompilation,
getPackagePartProvider = { environment.createPackagePartProvider(it) },
dependenciesConfigurator = {
dependencies(moduleConfiguration.jvmClasspathRoots.map { it.toPath() })
dependencies(moduleConfiguration.jvmModularRoots.map { it.toPath() })
friendDependencies(moduleConfiguration[JVMConfigurationKeys.FRIEND_PATHS] ?: emptyList())
},
sessionConfigurator = {
if (extendedAnalysisMode) {
registerExtendedCommonCheckers()
}
sourceScope
) {
if (commonSession != null) {
sourceDependsOnDependencies(listOf(commonSession.moduleData))
}
)
}
val commonAnalyzerFacade = commonSession?.let { FirAnalyzerFacade(it, languageVersionSettings, commonKtFiles) }
val firAnalyzerFacade = FirAnalyzerFacade(session, languageVersionSettings, ktFiles)
commonAnalyzerFacade?.runResolution()
val allFirDiagnostics = mutableListOf<FirDiagnostic>()
commonAnalyzerFacade?.runCheckers()?.values?.flattenTo(allFirDiagnostics)
firAnalyzerFacade.runResolution()
val firDiagnostics = firAnalyzerFacade.runCheckers().values.flatten()
val hasErrors = FirDiagnosticsCompilerResultsReporter.reportDiagnostics(firDiagnostics, environment.messageCollector)
firAnalyzerFacade.runCheckers().values.flattenTo(allFirDiagnostics)
val hasErrors = FirDiagnosticsCompilerResultsReporter.reportDiagnostics(allFirDiagnostics, environment.messageCollector)
return firAnalyzerFacade.takeUnless { syntaxErrors || hasErrors }
}
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
@@ -101,8 +102,13 @@ class FirAnalyzerFacade(
if (_scopeSession == null) runResolution()
val signaturer = JvmIdSignatureDescriptor(JvmDescriptorMangler(null))
val commonFirFiles = session.moduleData.dependsOnDependencies
.map { it.session }
.filter { it.kind == FirSession.Kind.Source }
.flatMap { (it.firProvider as FirProviderImpl).getAllFirFiles() }
return Fir2IrConverter.createModuleFragment(
session, _scopeSession!!, firFiles!!,
session, _scopeSession!!, firFiles!! + commonFirFiles,
languageVersionSettings, signaturer,
extensions, FirJvmKotlinMangler(session), IrFactoryImpl,
FirJvmVisibilityConverter,
@@ -290,6 +290,10 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
override fun getClassNamesInPackage(fqName: FqName): Set<Name> {
return state.classesInPackage[fqName] ?: emptySet()
}
fun getAllFirFiles(): List<FirFile> {
return state.fileMap.values.flatten()
}
}
private const val rebuildIndex = true
@@ -0,0 +1,10 @@
$TESTDATA_DIR$/firMultiplatformCompilationWithError/common.kt
$TESTDATA_DIR$/firMultiplatformCompilationWithError/jvm.kt
-Xcommon-sources
$TESTDATA_DIR$/firMultiplatformCompilationWithError/common.kt
-Xuse-fir
-cp
.
-d
$TEMP_DIR$
-XXLanguage\:+MultiPlatformProjects
@@ -0,0 +1,20 @@
warning: advanced option value is passed in an obsolete form. Please use the '=' character to specify the value: -Xcommon-sources=...
warning: ATTENTION!
This build uses unsafe internal compiler arguments:
-XXLanguage:+MultiPlatformProjects
This mode is not recommended for production use,
as no stability/compatibility guarantees are given on
compiler or generated code. Use it at your own risk!
warning: ATTENTION!
This build uses in-dev FIR:
-Xuse-fir
compiler/testData/cli/jvm/firMultiplatformCompilationWithError/jvm.kt:1:1: error: actual class 'A' has no corresponding members for expected class members:
/A.foo
actual interface A
^
COMPILATION_ERROR
@@ -0,0 +1,3 @@
expect interface A {
fun foo()
}
@@ -0,0 +1 @@
actual interface A
@@ -0,0 +1,10 @@
$TESTDATA_DIR$/firMultiplatformCompilationWithoutErrors/common.kt
$TESTDATA_DIR$/firMultiplatformCompilationWithoutErrors/jvm.kt
-Xcommon-sources
$TESTDATA_DIR$/firMultiplatformCompilationWithoutErrors/common.kt
-Xuse-fir
-cp
.
-d
$TEMP_DIR$
-XXLanguage\:+MultiPlatformProjects
@@ -0,0 +1,14 @@
warning: advanced option value is passed in an obsolete form. Please use the '=' character to specify the value: -Xcommon-sources=...
warning: ATTENTION!
This build uses unsafe internal compiler arguments:
-XXLanguage:+MultiPlatformProjects
This mode is not recommended for production use,
as no stability/compatibility guarantees are given on
compiler or generated code. Use it at your own risk!
warning: ATTENTION!
This build uses in-dev FIR:
-Xuse-fir
OK
@@ -0,0 +1,5 @@
expect interface A {
fun foo(): String
}
fun getA(): A = null!!
@@ -0,0 +1,9 @@
actual interface A {
actual fun foo(): String
fun bar(): String
}
fun test(): String {
val a = getA()
return a.foo() + a.bar()
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// !LANGUAGE: +MultiPlatformProjects
// ISSUE: KT-20306
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// !LANGUAGE: +MultiPlatformProjects
// ISSUE: KT-44474
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// !LANGUAGE: +MultiPlatformProjects
// ISSUE: KT-44474
@@ -5,7 +5,6 @@
// !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// MODULE: common
@@ -311,6 +311,16 @@ public class CliTestGenerated extends AbstractCliTest {
runTest("compiler/testData/cli/jvm/firHello.args");
}
@TestMetadata("firMultiplatformCompilationWithError.args")
public void testFirMultiplatformCompilationWithError() throws Exception {
runTest("compiler/testData/cli/jvm/firMultiplatformCompilationWithError.args");
}
@TestMetadata("firMultiplatformCompilationWithoutErrors.args")
public void testFirMultiplatformCompilationWithoutErrors() throws Exception {
runTest("compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.args");
}
@TestMetadata("firStdlibDependency.args")
public void testFirStdlibDependency() throws Exception {
runTest("compiler/testData/cli/jvm/firStdlibDependency.args");