diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests/org/jetbrains/kotlin/fir/java/AbstractFirOldFrontendLightClassesTest.kt b/compiler/fir/analysis-tests/legacy-fir-tests/tests/org/jetbrains/kotlin/fir/java/AbstractFirOldFrontendLightClassesTest.kt index 1d9b1977e10..39cc6665348 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests/org/jetbrains/kotlin/fir/java/AbstractFirOldFrontendLightClassesTest.kt +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests/org/jetbrains/kotlin/fir/java/AbstractFirOldFrontendLightClassesTest.kt @@ -1,24 +1,159 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2023 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.java +import com.intellij.openapi.vfs.StandardFileSystems +import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiElementFinder import com.intellij.psi.impl.compiled.ClsClassImpl import com.intellij.psi.search.GlobalSearchScope -import org.jetbrains.kotlin.fir.AbstractFirOldFrontendDiagnosticsTest +import org.jetbrains.kotlin.KtInMemoryTextSourceFile +import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.asJava.finder.JavaElementFinder +import org.jetbrains.kotlin.checkers.BaseDiagnosticsTest +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.compiler.PsiBasedProjectFileSearchScope +import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM +import org.jetbrains.kotlin.cli.jvm.compiler.VfsBasedProjectEnvironment +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.builder.BodyBuildingMode +import org.jetbrains.kotlin.fir.builder.PsiRawFirBuilder import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.doFirResolveTestBench +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.resolve.transformers.createAllCompilerResolveProcessors +import org.jetbrains.kotlin.fir.session.FirSessionFactoryHelper import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.jvm.JvmPlatforms +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.toSourceLinesMapping import java.io.File -abstract class AbstractFirOldFrontendLightClassesTest : AbstractFirOldFrontendDiagnosticsTest() { - override fun checkResultingFirFiles(firFiles: List, testDataFile: File) { - super.checkResultingFirFiles(firFiles, testDataFile) +abstract class AbstractFirOldFrontendLightClassesTest : BaseDiagnosticsTest() { + override fun analyzeAndCheck(testDataFile: File, files: List) { + if (files.any { "FIR_IGNORE" in it.directives }) return + try { + analyzeAndCheckUnhandled(testDataFile, files, useLightTree) + } catch (t: AssertionError) { + throw t + } catch (t: Throwable) { + throw t + } + } + private val useLightTree: Boolean + get() = false + + private val useLazyBodiesModeForRawFir: Boolean + get() = false + + override fun setupEnvironment(environment: KotlinCoreEnvironment) { + PsiElementFinder.EP.getPoint(environment.project).unregisterExtension(JavaElementFinder::class.java) + } + + private fun analyzeAndCheckUnhandled(testDataFile: File, files: List, useLightTree: Boolean = false) { + val groupedByModule = files.groupBy(TestFile::module) + + val modules = createModules(groupedByModule) + + val sessionProvider = FirProjectSessionProvider() + + //For BuiltIns, registered in sessionProvider automatically + val allProjectScope = GlobalSearchScope.allScope(project) + + val configToSession = modules.mapValues { (config, info) -> + val moduleFiles = groupedByModule.getValue(config) + val scope = TopDownAnalyzerFacadeForJVM.newModuleSearchScope( + project, + moduleFiles.mapNotNull { it.ktFile } + ) + val projectEnvironment = VfsBasedProjectEnvironment( + project, + VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) + ) { environment.createPackagePartProvider(it) } + + FirSessionFactoryHelper.createSessionWithDependencies( + Name.identifier(info.name.asString().removeSurrounding("<", ">")), + info.platform, + info.analyzerServices, + externalSessionProvider = sessionProvider, + projectEnvironment, + config?.languageVersionSettings ?: LanguageVersionSettingsImpl.DEFAULT, + javaSourcesScope = PsiBasedProjectFileSearchScope(scope), + librariesScope = PsiBasedProjectFileSearchScope(allProjectScope), + lookupTracker = null, + enumWhenTracker = null, + importTracker = null, + incrementalCompilationContext = null, + extensionRegistrars = emptyList(), + needRegisterJavaElementFinder = true + ) {} + } + + val firFilesPerSession = mutableMapOf>() + + // TODO: make module/session/transformer handling like in AbstractFirMultiModuleTest (IDE) + for ((testModule, testFilesInModule) in groupedByModule) { + val ktFiles = getKtFiles(testFilesInModule, true) + + val session = configToSession.getValue(testModule) + + val firFiles = mutableListOf() + mapKtFilesToFirFiles(session, ktFiles, firFiles, useLightTree) + firFilesPerSession[session] = firFiles + } + + runAnalysis(testDataFile, firFilesPerSession) + } + + private fun mapKtFilesToFirFiles(session: FirSession, ktFiles: List, firFiles: MutableList, useLightTree: Boolean) { + val firProvider = (session.firProvider as FirProviderImpl) + if (useLightTree) { + val lightTreeBuilder = LightTree2Fir(session, firProvider.kotlinScopeProvider) + ktFiles.mapTo(firFiles) { + val firFile = + lightTreeBuilder.buildFirFile( + it.text, + KtInMemoryTextSourceFile(it.name, it.virtualFilePath, it.text), + it.text.toSourceLinesMapping() + ) + (session.firProvider as FirProviderImpl).recordFile(firFile) + firFile + } + } else { + val firBuilder = PsiRawFirBuilder( + session, + firProvider.kotlinScopeProvider, + bodyBuildingMode = BodyBuildingMode.lazyBodies(useLazyBodiesModeForRawFir) + ) + ktFiles.mapTo(firFiles) { + val firFile = firBuilder.buildFirFile(it) + firProvider.recordFile(firFile) + firFile + } + } + } + + private fun runAnalysis(testDataFile: File, firFilesPerSession: Map>) { + for ((session, firFiles) in firFilesPerSession) { + doFirResolveTestBench(firFiles, createAllCompilerResolveProcessors(session), gc = false) + } + checkResultingFirFiles(testDataFile) + } + + private fun checkResultingFirFiles(testDataFile: File) { val ourFinders = PsiElementFinder.EP.getPoint(project).extensions.filterIsInstance() assertNotEmpty(ourFinders) @@ -52,4 +187,73 @@ abstract class AbstractFirOldFrontendLightClassesTest : AbstractFirOldFrontendDi override fun createTestFileFromPath(filePath: String): File { return File(filePath) } + + private fun createModules(groupedByModule: Map>): MutableMap { + val modules = + HashMap() + + for (testModule in groupedByModule.keys) { + val module = if (testModule == null) + createSealedModule() + else + createModule(testModule.name) + + modules[testModule] = module + } + + for (testModule in groupedByModule.keys) { + if (testModule == null) continue + + val module = modules[testModule]!! + val dependencies = ArrayList() + dependencies.add(module) + for (dependency in testModule.dependencies) { + dependencies.add(modules[dependency as TestModule?]!!) + } + + + dependencies.add(builtInsModuleInfo) + (module as TestModuleInfo).dependencies.addAll(dependencies) + } + + return modules + } + + private val builtInsModuleInfo = BuiltInModuleInfo(Name.special("")) + + private fun createModule(moduleName: String): TestModuleInfo { + parseModulePlatformByName(moduleName) + return TestModuleInfo(Name.special("<$moduleName>")) + } + + private class BuiltInModuleInfo(override val name: Name) : ModuleInfo { + override val platform: TargetPlatform + get() = JvmPlatforms.unspecifiedJvmPlatform + + override val analyzerServices: PlatformDependentAnalyzerServices + get() = JvmPlatformAnalyzerServices + + override fun dependencies(): List { + return listOf(this) + } + } + + private class TestModuleInfo(override val name: Name) : ModuleInfo { + override val platform: TargetPlatform + get() = JvmPlatforms.unspecifiedJvmPlatform + + override val analyzerServices: PlatformDependentAnalyzerServices + get() = JvmPlatformAnalyzerServices + + val dependencies = mutableListOf(this) + override fun dependencies(): List { + return dependencies + } + } + + private fun createSealedModule(): TestModuleInfo { + return createModule("test-module-jvm").apply { + dependencies += builtInsModuleInfo + } + } } diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirBaseDiagnosticsTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirBaseDiagnosticsTest.kt deleted file mode 100644 index 9546deeae07..00000000000 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirBaseDiagnosticsTest.kt +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright 2010-2019 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 - -import com.intellij.openapi.util.TextRange -import com.intellij.openapi.vfs.StandardFileSystems -import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiElementFinder -import com.intellij.psi.search.GlobalSearchScope -import org.jetbrains.kotlin.KtInMemoryTextSourceFile -import org.jetbrains.kotlin.analyzer.ModuleInfo -import org.jetbrains.kotlin.asJava.finder.JavaElementFinder -import org.jetbrains.kotlin.checkers.BaseDiagnosticsTest -import org.jetbrains.kotlin.checkers.DiagnosticDiffCallbacks -import org.jetbrains.kotlin.checkers.diagnostics.ActualDiagnostic -import org.jetbrains.kotlin.checkers.diagnostics.PositionalTextDiagnostic -import org.jetbrains.kotlin.checkers.diagnostics.SyntaxErrorDiagnostic -import org.jetbrains.kotlin.checkers.diagnostics.TextDiagnostic -import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.compiler.PsiBasedProjectFileSearchScope -import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM -import org.jetbrains.kotlin.cli.jvm.compiler.VfsBasedProjectEnvironment -import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl -import org.jetbrains.kotlin.diagnostics.Diagnostic -import org.jetbrains.kotlin.diagnostics.KtDiagnostic -import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils -import org.jetbrains.kotlin.fir.builder.BodyBuildingMode -import org.jetbrains.kotlin.fir.builder.PsiRawFirBuilder -import org.jetbrains.kotlin.fir.declarations.FirFile -import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider -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.FirSessionFactoryHelper -import org.jetbrains.kotlin.fir.session.FirSessionConfigurator -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.platform.TargetPlatform -import org.jetbrains.kotlin.platform.jvm.JvmPlatforms -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.resolve.AnalyzingUtils -import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices -import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices -import org.jetbrains.kotlin.toSourceLinesMapping -import java.io.File - -abstract class AbstractFirBaseDiagnosticsTest : BaseDiagnosticsTest() { - override fun analyzeAndCheck(testDataFile: File, files: List) { - try { - analyzeAndCheckUnhandled(testDataFile, files, useLightTree) - } catch (t: AssertionError) { - throw t - } catch (t: Throwable) { - throw t - } - } - - protected open val useLightTree: Boolean - get() = false - - protected open val useLazyBodiesModeForRawFir: Boolean - get() = false - - override fun setupEnvironment(environment: KotlinCoreEnvironment) { - PsiElementFinder.EP.getPoint(environment.project).unregisterExtension(JavaElementFinder::class.java) - } - - open fun analyzeAndCheckUnhandled(testDataFile: File, files: List, useLightTree: Boolean = false) { - val groupedByModule = files.groupBy(TestFile::module) - - val modules = createModules(groupedByModule) - - val sessionProvider = FirProjectSessionProvider() - - //For BuiltIns, registered in sessionProvider automatically - val allProjectScope = GlobalSearchScope.allScope(project) - - val configToSession = modules.mapValues { (config, info) -> - val moduleFiles = groupedByModule.getValue(config) - val scope = TopDownAnalyzerFacadeForJVM.newModuleSearchScope( - project, - moduleFiles.mapNotNull { it.ktFile }) - FirSessionFactoryHelper.createSessionWithDependencies( - Name.identifier(info.name.asString().removeSurrounding("<", ">")), - info.platform, - info.analyzerServices, - externalSessionProvider = sessionProvider, - VfsBasedProjectEnvironment( - project, VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL), - { environment.createPackagePartProvider(it) } - ), - config?.languageVersionSettings ?: LanguageVersionSettingsImpl.DEFAULT, - javaSourcesScope = PsiBasedProjectFileSearchScope(scope), - librariesScope = PsiBasedProjectFileSearchScope(allProjectScope), - lookupTracker = null, - enumWhenTracker = null, - importTracker = null, - incrementalCompilationContext = null, - extensionRegistrars = emptyList(), - needRegisterJavaElementFinder = true - ) { - configureSession() - } - } - - val firFilesPerSession = mutableMapOf>() - - // TODO: make module/session/transformer handling like in AbstractFirMultiModuleTest (IDE) - for ((testModule, testFilesInModule) in groupedByModule) { - val ktFiles = getKtFiles(testFilesInModule, true) - - val session = configToSession.getValue(testModule) - - val firFiles = mutableListOf() - mapKtFilesToFirFiles(session, ktFiles, firFiles, useLightTree) - firFilesPerSession[session] = firFiles - } - - runAnalysis(testDataFile, files, firFilesPerSession) - } - - private fun mapKtFilesToFirFiles(session: FirSession, ktFiles: List, firFiles: MutableList, useLightTree: Boolean) { - val firProvider = (session.firProvider as FirProviderImpl) - if (useLightTree) { - val lightTreeBuilder = LightTree2Fir(session, firProvider.kotlinScopeProvider) - ktFiles.mapTo(firFiles) { - val firFile = - lightTreeBuilder.buildFirFile( - it.text, - KtInMemoryTextSourceFile(it.name, it.virtualFilePath, it.text), - it.text.toSourceLinesMapping() - ) - (session.firProvider as FirProviderImpl).recordFile(firFile) - firFile - } - } else { - val firBuilder = PsiRawFirBuilder( - session, - firProvider.kotlinScopeProvider, - bodyBuildingMode = BodyBuildingMode.lazyBodies(useLazyBodiesModeForRawFir) - ) - ktFiles.mapTo(firFiles) { - val firFile = firBuilder.buildFirFile(it) - firProvider.recordFile(firFile) - firFile - } - } - } - - protected abstract fun runAnalysis(testDataFile: File, testFiles: List, firFilesPerSession: Map>) - - private fun createModules( - groupedByModule: Map> - ): MutableMap { - val modules = - HashMap() - - for (testModule in groupedByModule.keys) { - val module = if (testModule == null) - createSealedModule() - else - createModule(testModule.name) - - modules[testModule] = module - } - - for (testModule in groupedByModule.keys) { - if (testModule == null) continue - - val module = modules[testModule]!! - val dependencies = ArrayList() - dependencies.add(module) - for (dependency in testModule.dependencies) { - dependencies.add(modules[dependency as TestModule?]!!) - } - - - dependencies.add(builtInsModuleInfo) - //dependencies.addAll(getAdditionalDependencies(module)) - (module as TestModuleInfo).dependencies.addAll(dependencies) - } - - return modules - } - - private val builtInsModuleInfo = BuiltInModuleInfo(Name.special("")) - - protected open fun createModule(moduleName: String): TestModuleInfo { - parseModulePlatformByName(moduleName) - return TestModuleInfo(Name.special("<$moduleName>")) - } - - class BuiltInModuleInfo(override val name: Name) : - ModuleInfo { - override val platform: TargetPlatform - get() = JvmPlatforms.unspecifiedJvmPlatform - - override val analyzerServices: PlatformDependentAnalyzerServices - get() = JvmPlatformAnalyzerServices - - override fun dependencies(): List { - return listOf(this) - } - } - - protected class TestModuleInfo(override val name: Name) : - ModuleInfo { - override val platform: TargetPlatform - get() = JvmPlatforms.unspecifiedJvmPlatform - - override val analyzerServices: PlatformDependentAnalyzerServices - get() = JvmPlatformAnalyzerServices - - val dependencies = mutableListOf(this) - override fun dependencies(): List { - return dependencies - } - } - - protected open fun createSealedModule(): TestModuleInfo = - createModule("test-module-jvm").apply { - dependencies += builtInsModuleInfo - } - - protected fun TestFile.getActualText( - ktDiagnostics: Iterable, - actualText: StringBuilder - ): Boolean { - val ktFile = this.ktFile - if (ktFile == null) { - // TODO: check java files too - actualText.append(this.clearText) - return true - } - - if (ktFile.name.endsWith("CoroutineUtil.kt") && ktFile.packageFqName == FqName("helpers")) return true - - // TODO: report JVM signature diagnostics also for implementing modules - - val ok = booleanArrayOf(true) - val diagnostics = ktDiagnostics.toActualDiagnostic(ktFile) - val filteredDiagnostics = diagnostics // TODO - - actualDiagnostics.addAll(filteredDiagnostics) - - val uncheckedDiagnostics = mutableListOf() - - val diagnosticToExpectedDiagnostic = - CheckerTestUtil.diagnosticsDiff( - diagnosedRanges, - filteredDiagnostics, - object : DiagnosticDiffCallbacks { - override fun missingDiagnostic( - diagnostic: TextDiagnostic, - expectedStart: Int, - expectedEnd: Int - ) { - val message = - "Missing " + diagnostic.description + PsiDiagnosticUtils.atLocation( - ktFile, - TextRange( - expectedStart, - expectedEnd - ) - ) - System.err.println(message) - ok[0] = false - } - - override fun wrongParametersDiagnostic( - expectedDiagnostic: TextDiagnostic, - actualDiagnostic: TextDiagnostic, - start: Int, - end: Int - ) { - val message = "Parameters of diagnostic not equal at position " + - PsiDiagnosticUtils.atLocation( - ktFile, - TextRange( - start, - end - ) - ) + - ". Expected: ${expectedDiagnostic.asString()}, actual: $actualDiagnostic" - System.err.println(message) - ok[0] = false - } - - override fun unexpectedDiagnostic( - diagnostic: TextDiagnostic, - actualStart: Int, - actualEnd: Int - ) { - val message = - "Unexpected ${diagnostic.description}${PsiDiagnosticUtils.atLocation( - ktFile, - TextRange( - actualStart, - actualEnd - ) - )}" - System.err.println(message) - ok[0] = false - } - - fun updateUncheckedDiagnostics( - diagnostic: TextDiagnostic, - start: Int, - end: Int - ) { - uncheckedDiagnostics.add( - PositionalTextDiagnostic( - diagnostic, - start, - end - ) - ) - } - }) - - actualText.append( - CheckerTestUtil.addDiagnosticMarkersToText( - ktFile, - filteredDiagnostics, - diagnosticToExpectedDiagnostic, - { file -> file.text }, - uncheckedDiagnostics, - false, - false - ) - ) - - stripExtras(actualText) - - return ok[0] - } - - private fun Iterable.toActualDiagnostic(root: PsiElement): List { - val result = mutableListOf() - filterIsInstance().mapTo(result) { - ActualDiagnostic(it, null, true) - } - for (errorElement in AnalyzingUtils.getSyntaxErrorRanges(root)) { - result.add(ActualDiagnostic(SyntaxErrorDiagnostic(errorElement), null, true)) - } - return result - } - - protected open fun FirSessionConfigurator.configureSession() {} -} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirDiagnosticTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirDiagnosticTest.kt deleted file mode 100644 index 7e440bb5c70..00000000000 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirDiagnosticTest.kt +++ /dev/null @@ -1,339 +0,0 @@ -/* - * 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.fir - -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.* -import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1 -import org.jetbrains.kotlin.checkers.utils.TypeOfCall -import org.jetbrains.kotlin.diagnostics.* -import org.jetbrains.kotlin.diagnostics.rendering.Renderers -import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector -import org.jetbrains.kotlin.fir.analysis.collectors.FirDiagnosticsCollector -import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration -import org.jetbrains.kotlin.fir.declarations.FirFile -import org.jetbrains.kotlin.fir.declarations.FirFunction -import org.jetbrains.kotlin.fir.declarations.FirProperty -import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.references.FirNamedReference -import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference -import org.jetbrains.kotlin.fir.renderer.FirRenderer -import org.jetbrains.kotlin.fir.resolve.ScopeSession -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.FirControlFlowGraphRenderVisitor -import org.jetbrains.kotlin.fir.resolve.transformers.createAllCompilerResolveProcessors -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.SymbolInternals -import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.coneTypeOrNull -import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid -import org.jetbrains.kotlin.name.FqNameUnsafe -import org.jetbrains.kotlin.test.KotlinTestUtils -import org.jetbrains.kotlin.test.util.JUnit4Assertions -import org.jetbrains.kotlin.util.OperatorNameConventions -import org.jetbrains.kotlin.utils.addIfNotNull -import java.io.File - -/* - * For comfort viewing dumps of control flow graph you can setup external tool in IDEA that opens .dot files - * - * Example of config for `xdot` viewer: - * - * File -> Settings -> External tools -> Add - * - * Name: XDot - * Program: xdot - * Arguments: $FileNameWithoutExtension$.dot - * Working directory: $FileDir$ - * Disable "Open console for tool output" - * - * After that you can run action `XDot` in editor with source of test (or with cfg dump) - * and it will opens xdot with dump for that test - */ -@OptIn(SymbolInternals::class) -abstract class AbstractKtDiagnosticsTest : AbstractFirBaseDiagnosticsTest() { - companion object { - const val DUMP_CFG_DIRECTIVE = "DUMP_CFG" - - private val allowedKindsForDebugInfo = setOf( - KtRealSourceElementKind, - KtFakeSourceElementKind.DesugaredCompoundAssignment, - ) - - val TestFile.withDumpCfgDirective: Boolean - get() = DUMP_CFG_DIRECTIVE in directives - - val File.cfgDumpFile: File - get() = File(absolutePath.replace(".kt", ".dot")) - } - - override fun runAnalysis(testDataFile: File, testFiles: List, firFilesPerSession: Map>) { - for ((session, firFiles) in firFilesPerSession) { - doFirResolveTestBench( - firFiles, - createAllCompilerResolveProcessors(session), - gc = false - ) - } - val allFirFiles = firFilesPerSession.values.flatten() - checkDiagnostics(testDataFile, testFiles, allFirFiles) - checkFir(testDataFile, allFirFiles) - checkCfg(allFirFiles, testFiles, testDataFile) - } - - protected fun checkCfg( - allFirFiles: List, - testFiles: List, - testDataFile: File - ) { - checkCfgEdgeConsistency(allFirFiles) - if (testFiles.any { it.withDumpCfgDirective }) { - checkCfgDump(testDataFile, allFirFiles) - } else { - checkCfgDumpNotExists(testDataFile) - } - } - - fun checkFir(testDataFile: File, firFiles: List) { - val renderer = FirRenderer() - firFiles.forEach { renderer.renderElementAsString(it) } - val firFileDump = renderer.toString() - val expectedPath = testDataFile.absolutePath.replace(".kt", ".txt") - KotlinTestUtils.assertEqualsToFile( - File(expectedPath), - firFileDump - ) - } - - protected open fun checkDiagnostics(file: File, testFiles: List, firFiles: List) { - val diagnostics = collectDiagnostics(firFiles) - val actualTextBuilder = StringBuilder() - for (testFile in testFiles) { - val firFile = firFiles.firstOrNull { it.psi == testFile.ktFile } - if (firFile != null) { - val debugInfoDiagnostics: List = - collectDebugInfoDiagnostics(firFile, testFile.diagnosedRangesToDiagnosticNames) - testFile.getActualText( - diagnostics.getValue(firFile) + debugInfoDiagnostics, - actualTextBuilder, - ) - } else { - actualTextBuilder.append(testFile.expectedText) - } - } - val actualText = actualTextBuilder.toString() - KotlinTestUtils.assertEqualsToFile(file, actualText) - } - - private fun collectDebugInfoDiagnostics( - firFile: FirFile, - diagnosedRangesToDiagnosticNames: MutableMap> - ): List { - val result = mutableListOf() - - - object : FirDefaultVisitorVoid() { - override fun visitElement(element: FirElement) { - if (element is FirExpression) { - result.addIfNotNull( - createExpressionTypeDiagnosticIfExpected( - element, diagnosedRangesToDiagnosticNames - ) - ) - } - - element.acceptChildren(this) - } - - override fun visitFunctionCall(functionCall: FirFunctionCall) { - val reference = functionCall.calleeReference - result.addIfNotNull(createCallDiagnosticIfExpected(functionCall, reference, diagnosedRangesToDiagnosticNames)) - result.addIfNotNull(createDerivedClassDiagnosticIfExpected(functionCall, reference, diagnosedRangesToDiagnosticNames)) - - super.visitFunctionCall(functionCall) - } - - override fun visitDelegatedConstructorCall(delegatedConstructorCall: FirDelegatedConstructorCall) { - val reference = delegatedConstructorCall.calleeReference as FirNamedReference - result.addIfNotNull( - createDerivedClassDiagnosticIfExpected(delegatedConstructorCall, reference, diagnosedRangesToDiagnosticNames)) - - super.visitDelegatedConstructorCall(delegatedConstructorCall) - } - - override fun visitPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression) { - val reference = propertyAccessExpression.calleeReference as FirNamedReference - result.addIfNotNull( - createDerivedClassDiagnosticIfExpected(propertyAccessExpression, reference, diagnosedRangesToDiagnosticNames)) - } - }.let(firFile::accept) - - return result - } - - fun createExpressionTypeDiagnosticIfExpected( - element: FirExpression, - diagnosedRangesToDiagnosticNames: MutableMap> - ): KtDiagnosticWithParameters1? = - DebugInfoDiagnosticFactory1.EXPRESSION_TYPE.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) { - element.coneTypeOrNull.renderAsString((element as? FirSmartCastExpression)?.originalExpression?.coneTypeOrNull) - } - - private fun ConeKotlinType?.renderAsString(originalType: ConeKotlinType?): String { - val type = this ?: return "Type is unknown" - val rendered = type.renderForDebugInfo() - val originalTypeRendered = originalType?.renderForDebugInfo() ?: return rendered - - return "$rendered & $originalTypeRendered" - } - - private fun createCallDiagnosticIfExpected( - element: FirElement, - reference: FirNamedReference, - diagnosedRangesToDiagnosticNames: MutableMap> - ): KtDiagnostic? { - return DebugInfoDiagnosticFactory1.CALL.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) { - val resolvedSymbol = (reference as? FirResolvedNamedReference)?.resolvedSymbol - val fqName = resolvedSymbol?.fqNameUnsafe() - Renderers.renderCallInfo(fqName, getTypeOfCall(reference, resolvedSymbol)) - } - } - - private fun createDerivedClassDiagnosticIfExpected( - element: FirElement, - reference: FirNamedReference, - diagnosedRangesToDiagnosticNames: MutableMap> - ): KtDiagnostic? { - return DebugInfoDiagnosticFactory1.CALLABLE_OWNER.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) { - val resolvedSymbol = (reference as? FirResolvedNamedReference)?.resolvedSymbol - val callable = resolvedSymbol?.fir as? FirCallableDeclaration ?: return@createDebugInfoDiagnostic "" - DebugInfoDiagnosticFactory1.renderCallableOwner( - callable.symbol.callableId, - callable.containingClassLookupTag()?.classId, - callable.containingClassForStaticMemberAttr == null - ) - } - } - - private fun DebugInfoDiagnosticFactory1.createDebugInfoDiagnostic( - element: FirElement, - diagnosedRangesToDiagnosticNames: MutableMap>, - argument: () -> String, - ): KtDiagnosticWithParameters1? { - val sourceElement = element.source ?: return null - val sourceKind = sourceElement.kind - if (sourceKind !in allowedKindsForDebugInfo) { - if (sourceKind !is KtFakeSourceElementKind.ImplicitReturn || sourceElement.elementType != KtNodeTypes.RETURN) { - return null - } - } - // Lambda argument is always (?) duplicated by function literal - // Block expression is always (?) duplicated by single block expression - if (sourceElement.elementType == KtNodeTypes.LAMBDA_ARGUMENT || sourceElement.elementType == KtNodeTypes.BLOCK) return null - val name = name - if (diagnosedRangesToDiagnosticNames[sourceElement.startOffset..sourceElement.endOffset]?.contains(name) != true) return null - - val argumentText = argument() - return when (sourceElement) { - is KtPsiSourceElement -> KtPsiDiagnosticWithParameters1( - sourceElement, - argumentText, - severity, - KtDiagnosticFactory1(name, severity, AbstractSourceElementPositioningStrategy.DEFAULT, PsiElement::class), - AbstractSourceElementPositioningStrategy.DEFAULT - ) - is KtLightSourceElement -> KtLightDiagnosticWithParameters1( - sourceElement, - argumentText, - severity, - KtDiagnosticFactory1(name, severity, AbstractSourceElementPositioningStrategy.DEFAULT, PsiElement::class), - AbstractSourceElementPositioningStrategy.DEFAULT - ) - } - } - - private fun FirBasedSymbol<*>.fqNameUnsafe(): FqNameUnsafe? = when (this) { - is FirClassLikeSymbol<*> -> classId.asSingleFqName().toUnsafe() - is FirCallableSymbol<*> -> callableId.asFqNameForDebugInfo().toUnsafe() - else -> null - } - - private fun getTypeOfCall( - reference: FirNamedReference, - resolvedSymbol: FirBasedSymbol<*>? - ): String { - if (resolvedSymbol == null) return TypeOfCall.UNRESOLVED.nameToRender - - if ((resolvedSymbol as? FirFunctionSymbol)?.callableId?.callableName == OperatorNameConventions.INVOKE - && reference.name != OperatorNameConventions.INVOKE - ) { - return TypeOfCall.VARIABLE_THROUGH_INVOKE.nameToRender - } - - return when (val fir = resolvedSymbol.fir) { - is FirProperty -> { - TypeOfCall.PROPERTY_GETTER.nameToRender - } - is FirFunction -> buildString { - if (fir.status.isInline) append("inline ") - if (fir.status.isInfix) append("infix ") - if (fir.status.isOperator) append("operator ") - if (fir.receiverParameter != null) append("extension ") - append(TypeOfCall.FUNCTION.nameToRender) - } - else -> TypeOfCall.OTHER.nameToRender - } - } - - - protected fun collectDiagnostics(firFiles: List): Map> { - val collectors = mutableMapOf() - val result = mutableMapOf>() - for (firFile in firFiles) { - val session = firFile.moduleData.session - val collector = collectors.computeIfAbsent(session) { createCollector(session) } - val reporter = DiagnosticReporterFactory.createPendingReporter() - collector.collectDiagnostics(firFile, reporter) - result[firFile] = reporter.diagnostics - } - return result - } - - private fun createCollector(session: FirSession): AbstractDiagnosticCollector { - return FirDiagnosticsCollector.create( - session, - ScopeSession() - ) // seems this class is obsolete, so do not care about correctness of the scope session here - } - - private fun checkCfgDump(testDataFile: File, firFiles: List) { - val builder = StringBuilder() - - firFiles.first().accept(FirControlFlowGraphRenderVisitor(builder), null) - - val dotCfgDump = builder.toString() - KotlinTestUtils.assertEqualsToFile(testDataFile.cfgDumpFile, dotCfgDump) - } - - private fun checkCfgEdgeConsistency(firFiles: List) { - firFiles.forEach { it.accept(FirCfgConsistencyChecker(JUnit4Assertions)) } - } - - private fun checkCfgDumpNotExists(testDataFile: File) { - val cfgDumpFile = testDataFile.cfgDumpFile - if (cfgDumpFile.exists()) { - val message = """ - Directive `!$DUMP_CFG_DIRECTIVE` is missing, but file with cfg dump is present. - Please remove ${cfgDumpFile.path} or add `!$DUMP_CFG_DIRECTIVE` to test - """.trimIndent() - kotlin.test.fail(message) - } - - } - -} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirOldFrontendDiagnosticsTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirOldFrontendDiagnosticsTest.kt deleted file mode 100644 index 7d52d5ad2bc..00000000000 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirOldFrontendDiagnosticsTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2010-2019 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 - -import org.jetbrains.kotlin.fir.declarations.FirFile -import org.jetbrains.kotlin.fir.resolve.transformers.createAllCompilerResolveProcessors -import org.jetbrains.kotlin.test.KotlinTestUtils -import java.io.File - -abstract class AbstractFirOldFrontendDiagnosticsTest : AbstractKtDiagnosticsTest() { - override fun createTestFileFromPath(filePath: String): File { - val newPath = if (File(filePath).readText().contains("// FIR_IDENTICAL")) filePath else filePath.replace(".kt", ".fir.kt") - return File(newPath).also { - prepareTestDataFile(filePath, it) - } - } - - private fun prepareTestDataFile(originalFilePath: String, firTestDataFile: File) { - if (!firTestDataFile.exists()) { - KotlinTestUtils.assertEqualsToFile(firTestDataFile, loadTestDataWithDiagnostics(File(originalFilePath))) - } - } - - override fun analyzeAndCheck(testDataFile: File, files: List) { - if (files.any { "FIR_IGNORE" in it.directives }) return - super.analyzeAndCheck(testDataFile, files) - } - - override fun runAnalysis(testDataFile: File, testFiles: List, firFilesPerSession: Map>) { - val failure: FirRuntimeException? = try { - for ((session, firFiles) in firFilesPerSession) { - doFirResolveTestBench(firFiles, createAllCompilerResolveProcessors(session), gc = false) - } - null - } catch (e: FirRuntimeException) { - e - } - val failureFile = File(testDataFile.path.replace(".kt", ".fail")) - if (failure == null) { - val allFirFiles = firFilesPerSession.values.flatten() - checkResultingFirFiles(allFirFiles, testDataFile) - assertFalse("Test is good but there is expected exception", failureFile.exists()) - checkDiagnostics(testDataFile, testFiles, allFirFiles) - if (testDataFile.absolutePath.endsWith(".fir.kt")) { - val oldFrontendTestDataFile = File(testDataFile.absolutePath.replace(".fir.kt", ".kt")) - compareAndMergeFirFileAndOldFrontendFile(oldFrontendTestDataFile, testDataFile) - } - - val needDump = testFiles.any { "FIR_DUMP" in it.directives } - if (needDump) { - checkFir(testDataFile, allFirFiles) - } - checkCfg(allFirFiles, testFiles, testDataFile) - } else { - if (!failureFile.exists()) { - throw failure - } - checkFailureFile(failure, failureFile) - } - } - - private fun checkFailureFile(failure: FirRuntimeException, failureFile: File) { - val failureMessage = buildString { - appendLine(failure.message) - append("Cause: ") - appendLine(failure.cause) - } - KotlinTestUtils.assertEqualsToFile(failureFile, failureMessage) - } - - protected open fun checkResultingFirFiles(firFiles: List, testDataFile: File) {} -} - diff --git a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt index 069e873ff30..4af5ad369f4 100644 --- a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt +++ b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt @@ -15,10 +15,10 @@ import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.defaultConstructor.AbstractDefaultArgumentsReflectionTest import org.jetbrains.kotlin.codegen.flags.AbstractWriteFlagsTest import org.jetbrains.kotlin.codegen.ir.* +import org.jetbrains.kotlin.fir.java.AbstractFirOldFrontendLightClassesTest import org.jetbrains.kotlin.fir.builder.AbstractRawFirBuilderLazyBodiesTestCase import org.jetbrains.kotlin.fir.builder.AbstractRawFirBuilderSourceElementMappingTestCase import org.jetbrains.kotlin.fir.builder.AbstractRawFirBuilderTestCase -import org.jetbrains.kotlin.fir.java.AbstractFirOldFrontendLightClassesTest import org.jetbrains.kotlin.fir.java.AbstractFirTypeEnhancementTest import org.jetbrains.kotlin.fir.java.AbstractOwnFirTypeEnhancementTest import org.jetbrains.kotlin.fir.lightTree.AbstractLightTree2FirConverterTestCase