diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/analysis/FirAnalyzerFacade.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/analysis/FirAnalyzerFacade.kt index eb85554d70d..c4f7ace5a09 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/analysis/FirAnalyzerFacade.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/analysis/FirAnalyzerFacade.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.fir.analysis -import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.fir.FirSession @@ -37,7 +36,10 @@ class FirAnalyzerFacade( val useLightTree: Boolean = false ) { private var firFiles: List? = null - private var scopeSession: ScopeSession? = null + private var _scopeSession: ScopeSession? = null + val scopeSession: ScopeSession + get() = _scopeSession!! + private var collectedDiagnostics: Map>>? = null private fun buildRawFir() { @@ -62,16 +64,16 @@ class FirAnalyzerFacade( fun runResolution(): List { if (firFiles == null) buildRawFir() - if (scopeSession != null) return firFiles!! + if (_scopeSession != null) return firFiles!! val resolveProcessor = FirTotalResolveProcessor(session) resolveProcessor.process(firFiles!!) - scopeSession = resolveProcessor.scopeSession + _scopeSession = resolveProcessor.scopeSession return firFiles!! } @OptIn(ExperimentalStdlibApi::class) fun runCheckers(): Map>> { - if (scopeSession == null) runResolution() + if (_scopeSession == null) runResolution() if (collectedDiagnostics != null) return collectedDiagnostics!! val collector = FirDiagnosticsCollector.create(session) collectedDiagnostics = buildMap { @@ -83,11 +85,11 @@ class FirAnalyzerFacade( } fun convertToIr(extensions: GeneratorExtensions): Fir2IrResult { - if (scopeSession == null) runResolution() + if (_scopeSession == null) runResolution() val signaturer = JvmIdSignatureDescriptor(JvmManglerDesc()) return Fir2IrConverter.createModuleFragment( - session, scopeSession!!, firFiles!!, + session, _scopeSession!!, firFiles!!, languageVersionSettings, signaturer, extensions, FirJvmKotlinMangler(session), IrFactoryImpl, FirJvmVisibilityConverter, diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt index d65b634654f..e0bbd3b287b 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt @@ -54,6 +54,8 @@ class FirRenderer(builder: StringBuilder, private val mode: RenderMode = RenderM val renderCallableFqNames: Boolean, val renderDeclarationResolvePhase: Boolean, val renderAnnotation: Boolean, + val renderBodies: Boolean = true, + val renderPropertyAccessors: Boolean = true, ) { object Normal : RenderMode( renderLambdaBodies = true, @@ -86,6 +88,16 @@ class FirRenderer(builder: StringBuilder, private val mode: RenderMode = RenderM renderDeclarationResolvePhase = true, renderAnnotation = true, ) + + object NoBodies : RenderMode( + renderLambdaBodies = false, + renderCallArguments = false, + renderCallableFqNames = false, + renderDeclarationResolvePhase = false, + renderAnnotation = false, + renderBodies = false, + renderPropertyAccessors = false, + ) } private val printer = Printer(builder) @@ -442,6 +454,7 @@ class FirRenderer(builder: StringBuilder, private val mode: RenderMode = RenderM override fun visitProperty(property: FirProperty) { visitVariable(property) if (property.isLocal) return + if (!mode.renderPropertyAccessors) return println() pushIndent() property.getter?.accept(this) @@ -543,14 +556,17 @@ class FirRenderer(builder: StringBuilder, private val mode: RenderMode = RenderM anonymousInitializer.body?.renderBody() } - private fun FirBlock.renderBody(additionalStatements: List = emptyList()) = when (this) { - is FirLazyBlock -> { - println(" { LAZY_BLOCK }") - } - else -> renderInBraces { - for (statement in additionalStatements + statements) { - statement.accept(this@FirRenderer) - println() + private fun FirBlock.renderBody(additionalStatements: List = emptyList()) { + if (!mode.renderBodies) return + when (this) { + is FirLazyBlock -> { + println(" { LAZY_BLOCK }") + } + else -> renderInBraces { + for (statement in additionalStatements + statements) { + statement.accept(this@FirRenderer) + println() + } } } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt index a5fd73e6af8..8980161d453 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.fir.declarations -sealed class FirDeclarationOrigin(val fromSupertypes: Boolean = false) { +sealed class FirDeclarationOrigin(private val displayName: String? = null, val fromSupertypes: Boolean = false) { object Source : FirDeclarationOrigin() object Library : FirDeclarationOrigin() object BuiltIns : FirDeclarationOrigin() @@ -18,7 +18,11 @@ sealed class FirDeclarationOrigin(val fromSupertypes: Boolean = false) { object IntersectionOverride : FirDeclarationOrigin(fromSupertypes = true) object Delegated : FirDeclarationOrigin() - class Plugin(val key: FirPluginKey) : FirDeclarationOrigin() + class Plugin(val key: FirPluginKey) : FirDeclarationOrigin(displayName = "Plugin[$key]") + + override fun toString(): String { + return displayName ?: this::class.simpleName!! + } } abstract class FirPluginKey diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt index 573669b1c09..d061b982ed0 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.test.directives import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability.Global import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer +import org.jetbrains.kotlin.test.frontend.fir.handlers.FirScopeDumpHandler object FirDiagnosticsDirectives : SimpleDirectivesContainer() { val DUMP_CFG by directive( @@ -49,4 +50,11 @@ object FirDiagnosticsDirectives : SimpleDirectivesContainer() { val WITH_EXTENDED_CHECKERS by directive( description = "Enable extended checkers" ) + + val SCOPE_DUMP by stringDirective( + description = """ + Dump hierarchies of overrides of classes listed in arguments + Enables ${FirScopeDumpHandler::class} + """.trimIndent() + ) } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirScopeDumpHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirScopeDumpHandler.kt new file mode 100644 index 00000000000..42ba1f48364 --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirScopeDumpHandler.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2010-2021 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.test.frontend.fir.handlers + +import org.jetbrains.kotlin.fir.FirRenderer +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.render +import org.jetbrains.kotlin.fir.resolve.ScopeSession +import org.jetbrains.kotlin.fir.resolve.symbolProvider +import org.jetbrains.kotlin.fir.scopes.* +import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.types.render +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives +import org.jetbrains.kotlin.test.directives.model.DirectivesContainer +import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact +import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.test.services.moduleStructure +import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumperImpl +import org.jetbrains.kotlin.test.utils.withExtension +import org.jetbrains.kotlin.util.SmartPrinter +import org.jetbrains.kotlin.util.withIndent + +class FirScopeDumpHandler(testServices: TestServices) : FirAnalysisHandler(testServices) { + private val dumper = MultiModuleInfoDumperImpl() + + override val directivesContainers: List + get() = listOf(FirDiagnosticsDirectives) + + override fun processModule(module: TestModule, info: FirOutputArtifact) { + val fqNames = module.directives[FirDiagnosticsDirectives.SCOPE_DUMP] + if (fqNames.isEmpty()) return + val printer = SmartPrinter(dumper.builderForModule(module), indent = " ") + for (fqName in fqNames) { + printer.processClass(fqName, info.session, info.firAnalyzerFacade.scopeSession, module) + } + } + + private fun SmartPrinter.processClass(fqName: String, session: FirSession, scopeSession: ScopeSession, module: TestModule) { + val classId = ClassId.topLevel(FqName.fromSegments(fqName.split("."))) + val symbol = session.symbolProvider.getClassLikeSymbolByFqName(classId) ?: assertions.fail { + "Class $fqName not found in module ${module.name}" + } + val firClass = symbol.fir as? FirRegularClass ?: assertions.fail { "$fqName is not a class but ${symbol.fir.render()}" } + println("$fqName: ") + + val scope = firClass.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = true) + val names = scope.getCallableNames() + withIndent { + for (name in names) { + processFunctions(name, scope) + processProperties(name, scope) + } + } + println() + } + + private class SymbolCounter { + private val map = mutableMapOf, Int>() + private var counter = 0 + + fun getIndex(symbol: AbstractFirBasedSymbol<*>): Int { + return map.computeIfAbsent(symbol) { counter++ } + } + } + + private fun SmartPrinter.processFunctions(name: Name, scope: FirTypeScope) { + val functions = scope.getFunctions(name) + for (function in functions) { + processFunction(function, scope, SymbolCounter()) + println() + } + } + + private fun SmartPrinter.processFunction(symbol: FirNamedFunctionSymbol, scope: FirTypeScope, counter: SymbolCounter) { + printInfo(symbol.fir, counter) + scope.processDirectOverriddenFunctionsWithBaseScope(symbol) { overriden, baseScope -> + withIndent { + processFunction(overriden, baseScope, counter) + } + ProcessorAction.NEXT + } + } + + private fun SmartPrinter.processProperties(name: Name, scope: FirTypeScope) { + val properties = scope.getProperties(name) + for (property in properties) { + if (property !is FirPropertySymbol) continue + processProperty(property, scope, SymbolCounter()) + println() + } + } + + private fun SmartPrinter.processProperty(symbol: FirPropertySymbol, scope: FirTypeScope, counter: SymbolCounter) { + printInfo(symbol.fir, counter) + withIndent { + scope.processDirectOverriddenPropertiesWithBaseScope(symbol) { overriden, baseScope -> + processProperty(overriden, baseScope, counter) + ProcessorAction.NEXT + } + } + } + + private fun SmartPrinter.printInfo(declaration: FirCallableMemberDeclaration<*>, counter: SymbolCounter) { + print("[${declaration.origin}]: ") + print(declaration.render(FirRenderer.RenderMode.NoBodies).trim()) + print(" from ${declaration.dispatchReceiverType?.render()}") + println(" [id: ${counter.getIndex(declaration.symbol)}]") + } + + override fun processAfterAllModules(someAssertionWasFailed: Boolean) { + if (dumper.isEmpty()) return + val expectedFile = testServices.moduleStructure.originalTestDataFiles.first().withExtension(".overrides.txt") + val actualDump = dumper.generateResultingDump() + assertions.assertEqualsToFile(expectedFile, actualDump) + } +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractFirBlackBoxCodegenTest.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractFirBlackBoxCodegenTest.kt index 420286b2c29..d248484ff23 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractFirBlackBoxCodegenTest.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractFirBlackBoxCodegenTest.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.test.runners.codegen import org.jetbrains.kotlin.test.Constructor import org.jetbrains.kotlin.test.TargetBackend +import org.jetbrains.kotlin.test.backend.handlers.BytecodeListingHandler import org.jetbrains.kotlin.test.backend.handlers.IrTextDumpHandler import org.jetbrains.kotlin.test.backend.ir.JvmIrBackendFacade import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder @@ -16,6 +17,7 @@ import org.jetbrains.kotlin.test.frontend.fir.FirFrontendFacade import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact import org.jetbrains.kotlin.test.frontend.fir.handlers.FirCfgDumpHandler import org.jetbrains.kotlin.test.frontend.fir.handlers.FirDumpHandler +import org.jetbrains.kotlin.test.frontend.fir.handlers.FirScopeDumpHandler import org.jetbrains.kotlin.test.model.* open class AbstractFirBlackBoxCodegenTest : AbstractJvmBlackBoxCodegenTestBase( @@ -38,9 +40,10 @@ open class AbstractFirBlackBoxCodegenTest : AbstractJvmBlackBoxCodegenTestBase