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:
+19
-23
@@ -7,9 +7,10 @@ package org.jetbrains.kotlin.fir.lightTree
|
||||
|
||||
import com.intellij.lang.LighterASTNode
|
||||
import com.intellij.lang.impl.PsiBuilderFactoryImpl
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.util.diff.FlyweightCapableTreeStructure
|
||||
import org.jetbrains.kotlin.KtIoFileSourceFile
|
||||
import org.jetbrains.kotlin.KtSourceFile
|
||||
import org.jetbrains.kotlin.KtSourceFileLinesMapping
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticContext
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
|
||||
import org.jetbrains.kotlin.lexer.KotlinLexer
|
||||
import org.jetbrains.kotlin.parsing.KotlinLightParser
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
import org.jetbrains.kotlin.readSourceFileWithMapping
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
|
||||
@@ -46,7 +48,7 @@ class LightTree2Fir(
|
||||
return builder.lightTree
|
||||
}
|
||||
|
||||
fun buildLightTree(code: String): FlyweightCapableTreeStructure<LighterASTNode> {
|
||||
fun buildLightTree(code: CharSequence): FlyweightCapableTreeStructure<LighterASTNode> {
|
||||
val builder = PsiBuilderFactoryImpl().createBuilder(parserDefinition, makeLexer(), code)
|
||||
KotlinLightParser.parse(builder)
|
||||
return builder.lightTree
|
||||
@@ -58,25 +60,25 @@ class LightTree2Fir(
|
||||
}
|
||||
|
||||
fun buildFirFile(file: File): FirFile {
|
||||
val code = FileUtil.loadFile(file, CharsetToolkit.UTF8, true)
|
||||
return buildFirFile(code, file.name, file.path)
|
||||
val sourceFile = KtIoFileSourceFile(file)
|
||||
val (code, linesMapping) = with(file.inputStream().reader(Charsets.UTF_8)) {
|
||||
this.readSourceFileWithMapping()
|
||||
}
|
||||
return buildFirFile(code, sourceFile, linesMapping)
|
||||
}
|
||||
|
||||
fun buildFirFile(lightTreeFile: LightTreeFile): FirFile = with(lightTreeFile) {
|
||||
fun buildFirFile(
|
||||
lightTree: FlyweightCapableTreeStructure<LighterASTNode>,
|
||||
sourceFile: KtSourceFile,
|
||||
linesMapping: KtSourceFileLinesMapping
|
||||
): FirFile =
|
||||
DeclarationsConverter(
|
||||
session, scopeProvider, lightTree, diagnosticsReporter = diagnosticsReporter,
|
||||
diagnosticContext = makeDiagnosticContext(path)
|
||||
).convertFile(lightTree.root, fileName, path)
|
||||
}
|
||||
diagnosticContext = makeDiagnosticContext(sourceFile.path)
|
||||
).convertFile(lightTree.root, sourceFile, linesMapping)
|
||||
|
||||
fun buildFirFile(code: String, fileName: String, path: String?): FirFile {
|
||||
val lightTree = buildLightTree(code)
|
||||
|
||||
return DeclarationsConverter(
|
||||
session, scopeProvider, lightTree, diagnosticsReporter = diagnosticsReporter,
|
||||
diagnosticContext = makeDiagnosticContext(path)
|
||||
).convertFile(lightTree.root, fileName, path)
|
||||
}
|
||||
fun buildFirFile(code: CharSequence, sourceFile: KtSourceFile, linesMapping: KtSourceFileLinesMapping): FirFile =
|
||||
buildFirFile(buildLightTree(code), sourceFile, linesMapping)
|
||||
|
||||
private fun makeDiagnosticContext(path: String?) =
|
||||
if (diagnosticsReporter == null) null else object : DiagnosticContext {
|
||||
@@ -86,9 +88,3 @@ class LightTree2Fir(
|
||||
}
|
||||
}
|
||||
|
||||
data class LightTreeFile(
|
||||
val lightTree: FlyweightCapableTreeStructure<LighterASTNode>,
|
||||
val fileName: String,
|
||||
val path: String?
|
||||
)
|
||||
|
||||
|
||||
+4
-3
@@ -88,7 +88,7 @@ class DeclarationsConverter(
|
||||
* [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile]
|
||||
* [org.jetbrains.kotlin.parsing.KotlinParsing.parsePreamble]
|
||||
*/
|
||||
fun convertFile(file: LighterASTNode, fileName: String = "", filePath: String?): FirFile {
|
||||
fun convertFile(file: LighterASTNode, sourceFile: KtSourceFile, linesMapping: KtSourceFileLinesMapping): FirFile {
|
||||
if (file.tokenType != KT_FILE) {
|
||||
//TODO throw error
|
||||
throw Exception()
|
||||
@@ -123,8 +123,9 @@ class DeclarationsConverter(
|
||||
source = file.toFirSourceElement()
|
||||
origin = FirDeclarationOrigin.Source
|
||||
moduleData = baseModuleData
|
||||
name = fileName
|
||||
path = filePath
|
||||
name = sourceFile.name
|
||||
this.sourceFile = sourceFile
|
||||
this.sourceFileLinesMapping = linesMapping
|
||||
this.packageDirective = packageDirective ?: buildPackageDirective { packageFqName = context.packageFqName }
|
||||
annotations += fileAnnotationList
|
||||
imports += importList
|
||||
|
||||
+14
-4
@@ -10,11 +10,15 @@ import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.psi.impl.DebugUtil
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.kotlin.KtIoFileSourceFile
|
||||
import org.jetbrains.kotlin.KtSourceFile
|
||||
import org.jetbrains.kotlin.KtSourceFileLinesMapping
|
||||
import org.jetbrains.kotlin.fir.FirRenderer
|
||||
import org.jetbrains.kotlin.fir.builder.AbstractRawFirBuilderTestCase
|
||||
import org.jetbrains.kotlin.fir.builder.StubFirScopeProvider
|
||||
import org.jetbrains.kotlin.fir.session.FirSessionFactory
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.readSourceFileWithMapping
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
@@ -33,12 +37,15 @@ class TotalKotlinTest : AbstractRawFirBuilderTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateFirFromLightTree(onlyLightTree: Boolean, converter: LightTree2Fir, text: String, fileName: String, filePath: String) {
|
||||
private fun generateFirFromLightTree(
|
||||
onlyLightTree: Boolean, converter: LightTree2Fir,
|
||||
text: CharSequence, sourceFile: KtSourceFile, linesMapping: KtSourceFileLinesMapping
|
||||
) {
|
||||
if (onlyLightTree) {
|
||||
val lightTree = LightTree2Fir.buildLightTree(text)
|
||||
DebugUtil.lightTreeToString(lightTree, false)
|
||||
} else {
|
||||
val firFile = converter.buildFirFile(text, fileName, filePath)
|
||||
val firFile = converter.buildFirFile(text, sourceFile, linesMapping)
|
||||
StringBuilder().also { FirRenderer(it).visitFile(firFile) }.toString()
|
||||
}
|
||||
}
|
||||
@@ -57,9 +64,12 @@ class TotalKotlinTest : AbstractRawFirBuilderTestCase() {
|
||||
if (onlyLightTree) println("LightTree generation") else println("Fir from LightTree converter")
|
||||
println("BASE PATH: $path")
|
||||
path.walkTopDown {
|
||||
val text = FileUtil.loadFile(it, CharsetToolkit.UTF8, true).trim()
|
||||
val sourceFile = KtIoFileSourceFile(it)
|
||||
val (code, linesMapping) = with(it.inputStream().reader(Charsets.UTF_8)) {
|
||||
this.readSourceFileWithMapping()
|
||||
}
|
||||
time += measureNanoTime {
|
||||
generateFirFromLightTree(onlyLightTree, lightTreeConverter, text, it.name, it.path)
|
||||
generateFirFromLightTree(onlyLightTree, lightTreeConverter, code, sourceFile, linesMapping)
|
||||
}
|
||||
|
||||
counter++
|
||||
|
||||
+15
-4
@@ -10,6 +10,8 @@ import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
import com.intellij.util.PathUtil
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.KtInMemoryTextSourceFile
|
||||
import org.jetbrains.kotlin.KtIoFileSourceFile
|
||||
import org.jetbrains.kotlin.checkers.BaseDiagnosticsTest.Companion.DIAGNOSTIC_IN_TESTDATA_PATTERN
|
||||
import org.jetbrains.kotlin.fir.FirRenderer
|
||||
import org.jetbrains.kotlin.fir.builder.AbstractRawFirBuilderTestCase
|
||||
@@ -19,7 +21,9 @@ import org.jetbrains.kotlin.fir.lightTree.walkTopDown
|
||||
import org.jetbrains.kotlin.fir.lightTree.walkTopDownWithTestData
|
||||
import org.jetbrains.kotlin.fir.session.FirSessionFactory
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.readSourceFileWithMapping
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners
|
||||
import org.jetbrains.kotlin.toSourceLinesMapping
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
|
||||
@@ -61,15 +65,17 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
|
||||
diagnosticsReporter = null
|
||||
)
|
||||
compareBase(System.getProperty("user.dir"), withTestData = false) { file ->
|
||||
val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
|
||||
val (text, linesMapping) = with(file.inputStream().reader(Charsets.UTF_8)) {
|
||||
this.readSourceFileWithMapping()
|
||||
}
|
||||
|
||||
//psi
|
||||
val ktFile = createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.path)), text) as KtFile
|
||||
val ktFile = createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.path)), text.toString().trim()) as KtFile
|
||||
val firFileFromPsi = ktFile.toFirFile()
|
||||
val treeFromPsi = StringBuilder().also { FirRenderer(it).visitFile(firFileFromPsi) }.toString()
|
||||
|
||||
//light tree
|
||||
val firFileFromLightTree = lightTreeConverter.buildFirFile(text, file.name, file.path)
|
||||
val firFileFromLightTree = lightTreeConverter.buildFirFile(text, KtIoFileSourceFile(file), linesMapping)
|
||||
val treeFromLightTree = StringBuilder().also { FirRenderer(it).visitFile(firFileFromLightTree) }.toString()
|
||||
|
||||
return@compareBase treeFromLightTree == treeFromPsi
|
||||
@@ -100,7 +106,12 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
|
||||
.replace("<Unsupported LValue.*?>".toRegex(), "<Unsupported LValue>")
|
||||
|
||||
//light tree
|
||||
val firFileFromLightTree = lightTreeConverter.buildFirFile(text, file.name, file.path)
|
||||
val firFileFromLightTree =
|
||||
lightTreeConverter.buildFirFile(
|
||||
text,
|
||||
KtInMemoryTextSourceFile(file.name, file.path, text),
|
||||
text.toSourceLinesMapping()
|
||||
)
|
||||
val treeFromLightTree = StringBuilder().also { FirRenderer(it).visitFile(firFileFromLightTree) }.toString()
|
||||
.replace("<Unsupported LValue.*?>".toRegex(), "<Unsupported LValue>")
|
||||
|
||||
|
||||
@@ -911,7 +911,8 @@ open class RawFirBuilder(
|
||||
moduleData = baseModuleData
|
||||
origin = FirDeclarationOrigin.Source
|
||||
name = file.name
|
||||
path = file.virtualFile?.path
|
||||
sourceFile = KtPsiSourceFile(file)
|
||||
sourceFileLinesMapping = KtPsiSourceFileLinesMapping(file)
|
||||
packageDirective = buildPackageDirective {
|
||||
packageFqName = context.packageFqName
|
||||
source = file.packageDirective?.toKtPsiSourceElement()
|
||||
|
||||
Reference in New Issue
Block a user