FIR LT: Introduce source file abstraction, carry it from parsing to IR

along with source lines mapping, allows to "emulate" usage of the
PSI files which allows to extract source file and line mapping info
on every stage from source element.
It makes sense to use this mapping for the error reporting too.
This commit is contained in:
Ilya Chernikov
2022-02-19 21:07:21 +01:00
committed by teamcity
parent bd60d4b2a6
commit 03cbfea737
45 changed files with 539 additions and 239 deletions
@@ -1,123 +0,0 @@
/*
* 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.fir.analysis
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.collectors.FirDiagnosticsCollector
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.fir.backend.Fir2IrConverter
import org.jetbrains.kotlin.fir.backend.Fir2IrResult
import org.jetbrains.kotlin.fir.backend.jvm.Fir2IrJvmSpecialAnnotationSymbolProvider
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmKotlinMangler
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmVisibilityConverter
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.lightTree.LightTreeFile
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveProcessor
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
abstract class AbstractFirAnalyzerFacade {
abstract val scopeSession: ScopeSession
abstract fun runCheckers(): Map<FirFile, List<KtDiagnostic>>
abstract fun runResolution(): List<FirFile>
abstract fun convertToIr(extensions: GeneratorExtensions): Fir2IrResult
}
class FirAnalyzerFacade(
val session: FirSession,
val languageVersionSettings: LanguageVersionSettings,
val ktFiles: Collection<KtFile> = emptyList(), // may be empty if light tree mode enabled
val lightTreeFiles: Collection<LightTreeFile> = emptyList(), // may be empty if light tree mode disabled
val irGeneratorExtensions: Collection<IrGenerationExtension>,
val useLightTree: Boolean = false,
val enablePluginPhases: Boolean = false,
) : AbstractFirAnalyzerFacade() {
private var firFiles: List<FirFile>? = null
private var _scopeSession: ScopeSession? = null
override val scopeSession: ScopeSession
get() = _scopeSession!!
private var collectedDiagnostics: Map<FirFile, List<KtDiagnostic>>? = null
private fun buildRawFir() {
if (firFiles != null) return
val firProvider = (session.firProvider as FirProviderImpl)
firFiles = if (useLightTree) {
val builder = LightTree2Fir(session, firProvider.kotlinScopeProvider)
lightTreeFiles.map {
builder.buildFirFile(it).also { firFile ->
firProvider.recordFile(firFile)
}
}
} else {
val builder = RawFirBuilder(session, firProvider.kotlinScopeProvider, PsiHandlingMode.COMPILER)
ktFiles.map {
builder.buildFirFile(it).also { firFile ->
firProvider.recordFile(firFile)
}
}
}
}
override fun runResolution(): List<FirFile> {
if (firFiles == null) buildRawFir()
if (_scopeSession != null) return firFiles!!
val resolveProcessor = FirTotalResolveProcessor(session)
resolveProcessor.process(firFiles!!)
_scopeSession = resolveProcessor.scopeSession
return firFiles!!
}
@OptIn(ExperimentalStdlibApi::class)
override fun runCheckers(): Map<FirFile, List<KtDiagnostic>> {
if (_scopeSession == null) runResolution()
if (collectedDiagnostics != null) return collectedDiagnostics!!
val collector = FirDiagnosticsCollector.create(session, scopeSession)
collectedDiagnostics = buildMap {
for (file in firFiles!!) {
val reporter = DiagnosticReporterFactory.createReporter()
collector.collectDiagnostics(file, reporter)
put(file, reporter.diagnostics)
}
}
return collectedDiagnostics!!
}
override fun convertToIr(extensions: GeneratorExtensions): Fir2IrResult {
if (_scopeSession == null) runResolution()
val mangler = JvmDescriptorMangler(null)
val signaturer = JvmIdSignatureDescriptor(mangler)
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!! + commonFirFiles,
languageVersionSettings, mangler, signaturer,
extensions, FirJvmKotlinMangler(session), IrFactoryImpl,
FirJvmVisibilityConverter,
Fir2IrJvmSpecialAnnotationSymbolProvider(),
irGeneratorExtensions
)
}
}
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.fir.pipeline
import com.intellij.openapi.util.text.StringUtilRt
import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
@@ -14,15 +14,12 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
import org.jetbrains.kotlin.fir.session.environment.AbstractProjectEnvironment
import org.jetbrains.kotlin.fir.session.sourcesToPathsMapper
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
import java.io.FileNotFoundException
import org.jetbrains.kotlin.readSourceFileWithMapping
fun FirSession.buildFirViaLightTree(
files: Collection<File>,
projectEnvironment: AbstractProjectEnvironment,
files: Collection<KtSourceFile>,
diagnosticsReporter: DiagnosticReporter? = null,
reportFilesAndLines: ((Int, Int) -> Unit)? = null
): List<FirFile> {
@@ -32,14 +29,15 @@ fun FirSession.buildFirViaLightTree(
val shouldCountLines = (reportFilesAndLines != null)
var linesCount = 0
val firFiles = files.map { file ->
val text = projectEnvironment.getFileText(file.absolutePath) ?: throw FileNotFoundException(file.path)
val code = StringUtilRt.convertLineSeparators(text)
if (shouldCountLines) {
linesCount += code.count { it == '\n' } // assuming converted line separators
val (code, linesMapping) = file.getContentsAsStream().reader(Charsets.UTF_8).use {
it.readSourceFileWithMapping()
}
builder.buildFirFile(code, file.name, file.path).also { firFile ->
if (shouldCountLines) {
linesCount += linesMapping.lastOffset
}
builder.buildFirFile(code, file, linesMapping).also { firFile ->
firProvider.recordFile(firFile)
sourcesToPathsMapper.registerFileSource(firFile.source!!, file.path)
sourcesToPathsMapper.registerFileSource(firFile.source!!, file.path ?: file.name)
}
}
reportFilesAndLines?.invoke(files.count(), linesCount)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.session.environment
import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.java.FirJavaFacade
@@ -49,6 +50,8 @@ interface AbstractProjectEnvironment {
fun getSearchScopeByIoFiles(files: Iterable<File>, allowOutOfProjectRoots: Boolean = false): AbstractProjectFileSearchScope
fun getSearchScopeBySourceFiles(files: Iterable<KtSourceFile>, allowOutOfProjectRoots: Boolean = false): AbstractProjectFileSearchScope
fun getSearchScopeByDirectories(directories: Iterable<File>): AbstractProjectFileSearchScope
fun getSearchScopeForProjectLibraries(): AbstractProjectFileSearchScope
@@ -60,6 +63,4 @@ interface AbstractProjectEnvironment {
baseModuleData: FirModuleData,
fileSearchScope: AbstractProjectFileSearchScope
): FirJavaFacade
fun getFileText(filePath: String): String?
}