Introduce LT file abstraction in tests parallel to KtFile

allows to create a valid LT-based file with synthetic path important
for dumps comparisons in tests
This commit is contained in:
Ilya Chernikov
2021-11-03 11:09:12 +01:00
committed by teamcity
parent 20c50c6484
commit cf22400701
5 changed files with 41 additions and 11 deletions
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.builder.RawFirBuilder import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir 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.moduleData
import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.resolve.providers.firProvider
@@ -30,7 +31,6 @@ import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import java.io.File
abstract class AbstractFirAnalyzerFacade { abstract class AbstractFirAnalyzerFacade {
abstract val scopeSession: ScopeSession abstract val scopeSession: ScopeSession
@@ -45,7 +45,7 @@ class FirAnalyzerFacade(
val session: FirSession, val session: FirSession,
val languageVersionSettings: LanguageVersionSettings, val languageVersionSettings: LanguageVersionSettings,
val ktFiles: Collection<KtFile> = emptyList(), // may be empty if light tree mode enabled val ktFiles: Collection<KtFile> = emptyList(), // may be empty if light tree mode enabled
val originalFiles: Collection<File> = emptyList(), // may be empty if light tree mode disabled val lightTreeFiles: Collection<LightTreeFile> = emptyList(), // may be empty if light tree mode disabled
val irGeneratorExtensions: Collection<IrGenerationExtension>, val irGeneratorExtensions: Collection<IrGenerationExtension>,
val useLightTree: Boolean = false, val useLightTree: Boolean = false,
val enablePluginPhases: Boolean = false, val enablePluginPhases: Boolean = false,
@@ -62,7 +62,7 @@ class FirAnalyzerFacade(
val firProvider = (session.firProvider as FirProviderImpl) val firProvider = (session.firProvider as FirProviderImpl)
firFiles = if (useLightTree) { firFiles = if (useLightTree) {
val builder = LightTree2Fir(session, firProvider.kotlinScopeProvider) val builder = LightTree2Fir(session, firProvider.kotlinScopeProvider)
originalFiles.map { lightTreeFiles.map {
builder.buildFirFile(it).also { firFile -> builder.buildFirFile(it).also { firFile ->
firProvider.recordFile(firFile) firProvider.recordFile(firFile)
} }
@@ -39,6 +39,12 @@ class LightTree2Fir(
KotlinLightParser.parseLambdaExpression(builder) KotlinLightParser.parseLambdaExpression(builder)
return builder.lightTree return builder.lightTree
} }
fun buildLightTree(code: String): FlyweightCapableTreeStructure<LighterASTNode> {
val builder = PsiBuilderFactoryImpl().createBuilder(parserDefinition, makeLexer(), code)
KotlinLightParser.parse(builder)
return builder.lightTree
}
} }
fun buildFirFile(path: Path): FirFile { fun buildFirFile(path: Path): FirFile {
@@ -50,10 +56,11 @@ class LightTree2Fir(
return buildFirFile(code, file.name, file.path) return buildFirFile(code, file.name, file.path)
} }
fun buildLightTree(code: String): FlyweightCapableTreeStructure<LighterASTNode> { fun buildFirFile(lightTreeFile: LightTreeFile): FirFile = with(lightTreeFile) {
val builder = PsiBuilderFactoryImpl().createBuilder(parserDefinition, makeLexer(), code) DeclarationsConverter(
KotlinLightParser.parse(builder) session, scopeProvider, lightTree, diagnosticsReporter = diagnosticsReporter,
return builder.lightTree diagnosticContext = makeDiagnosticContext(path)
).convertFile(lightTree.root, fileName, path)
} }
fun buildFirFile(code: String, fileName: String, path: String?): FirFile { fun buildFirFile(code: String, fileName: String, path: String?): FirFile {
@@ -63,3 +70,10 @@ class LightTree2Fir(
.convertFile(lightTree.root, fileName, path) .convertFile(lightTree.root, fileName, path)
} }
} }
data class LightTreeFile(
val lightTree: FlyweightCapableTreeStructure<LighterASTNode>,
val fileName: String,
val path: String?
)
@@ -35,7 +35,7 @@ class TotalKotlinTest : AbstractRawFirBuilderTestCase() {
private fun generateFirFromLightTree(onlyLightTree: Boolean, converter: LightTree2Fir, text: String, fileName: String, filePath: String) { private fun generateFirFromLightTree(onlyLightTree: Boolean, converter: LightTree2Fir, text: String, fileName: String, filePath: String) {
if (onlyLightTree) { if (onlyLightTree) {
val lightTree = converter.buildLightTree(text) val lightTree = LightTree2Fir.buildLightTree(text)
DebugUtil.lightTreeToString(lightTree, false) DebugUtil.lightTreeToString(lightTree, false)
} else { } else {
val firFile = converter.buildFirFile(text, fileName, filePath) val firFile = converter.buildFirFile(text, fileName, filePath)
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.test.services package org.jetbrains.kotlin.test.services
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir
import org.jetbrains.kotlin.fir.lightTree.LightTreeFile
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestFile import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.util.KtTestUtil import org.jetbrains.kotlin.test.util.KtTestUtil
@@ -97,6 +99,20 @@ fun SourceFileProvider.getKtFilesForSourceFiles(testFiles: Collection<TestFile>,
}.toMap() }.toMap()
} }
fun SourceFileProvider.getLightTreeKtFileForSourceFile(testFile: TestFile): LightTreeFile {
val shortName = testFile.name.substringAfterLast('/').substringAfterLast('\\')
val file = getRealFileForSourceFile(testFile)
val lightTree = LightTree2Fir.buildLightTree(file.readText())
return LightTreeFile(lightTree, shortName, "/$shortName") // emulating behavior of KtTestUtil.createFile so path looks the same in testdata
}
fun SourceFileProvider.getLightTreeFilesForSourceFiles(testFiles: Collection<TestFile>): Map<TestFile, LightTreeFile> {
return testFiles.mapNotNull {
if (!it.isKtFile) return@mapNotNull null
it to getLightTreeKtFileForSourceFile(it)
}.toMap()
}
val TestFile.isKtFile: Boolean val TestFile.isKtFile: Boolean
get() = name.endsWith(".kt") || name.endsWith(".kts") get() = name.endsWith(".kt") || name.endsWith(".kts")
@@ -66,8 +66,8 @@ class FirFrontendFacade(
PsiElementFinder.EP.getPoint(project).unregisterExtension(JavaElementFinder::class.java) PsiElementFinder.EP.getPoint(project).unregisterExtension(JavaElementFinder::class.java)
val lightTreeEnabled = FirDiagnosticsDirectives.USE_LIGHT_TREE in module.directives val lightTreeEnabled = FirDiagnosticsDirectives.USE_LIGHT_TREE in module.directives
val (ktFiles, originalFiles) = if (lightTreeEnabled) { val (ktFiles, lightTreeFiles) = if (lightTreeEnabled) {
emptyList<KtFile>() to module.files.filter { it.isKtFile }.map { testServices.sourceFileProvider.getRealFileForSourceFile(it) } emptyList<KtFile>() to testServices.sourceFileProvider.getLightTreeFilesForSourceFiles(module.files).values
} else { } else {
testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project).values to emptyList() testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project).values to emptyList()
} }
@@ -120,7 +120,7 @@ class FirFrontendFacade(
session, session,
languageVersionSettings, languageVersionSettings,
ktFiles, ktFiles,
originalFiles, lightTreeFiles,
IrGenerationExtension.getInstances(project), IrGenerationExtension.getInstances(project),
lightTreeEnabled, lightTreeEnabled,
enablePluginPhases enablePluginPhases