diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedDeclarationsProcessor.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedDeclarationsProcessor.kt index 24b8da4fec0..faf3a844377 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedDeclarationsProcessor.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedDeclarationsProcessor.kt @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.backend.common.serialization.unlinked import org.jetbrains.kotlin.backend.common.serialization.unlinked.PartialLinkageSupport.UnlinkedMarkerTypeHandler +import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedIrElementRenderer.appendDeclaration +import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedIrElementRenderer.renderError import org.jetbrains.kotlin.backend.common.serialization.unlinked.UsedClassifierSymbolStatus.Companion.isUnlinked import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrBuiltIns @@ -22,9 +24,10 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrTypeProjection import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.util.IrMessageLogger.Location +import org.jetbrains.kotlin.ir.util.IrMessageLogger.Severity import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addIfNotNull internal class UnlinkedDeclarationsProcessor( private val builtIns: IrBuiltIns, @@ -32,14 +35,9 @@ internal class UnlinkedDeclarationsProcessor( private val unlinkedMarkerTypeHandler: UnlinkedMarkerTypeHandler, private val messageLogger: IrMessageLogger ) { - companion object { - private val ERROR_ORIGIN = object : IrStatementOriginImpl("LINKAGE ERROR") {} - } - fun addLinkageErrorIntoUnlinkedClasses() { usedClassifierSymbols.forEachClassSymbolToPatch { unlinkedSymbol -> val clazz = unlinkedSymbol.owner - clazz.reportUnlinkedSymbolsWarning("Class", clazz.fqNameForIrSerialization) val anonInitializer = clazz.declarations.firstNotNullOfOrNull { it as? IrAnonymousInitializer } ?: builtIns.irFactory.createAnonymousInitializer( @@ -53,38 +51,12 @@ internal class UnlinkedDeclarationsProcessor( clazz.declarations.add(it) } anonInitializer.body.statements.clear() - anonInitializer.body.statements.add(clazz.throwLinkageError(clazz.symbol)) + anonInitializer.body.statements += clazz.throwLinkageError() // TODO: which exactly classifiers are unlinked? clazz.superTypes = clazz.superTypes.filter { !it.isUnlinked() } } } - private fun IrDeclaration.location(): IrMessageLogger.Location? = fileOrNull?.location(startOffset) - - private fun IrFile.location(offset: Int): IrMessageLogger.Location { - val module = module.name - val fileEntry = fileEntry - val fileName = fileEntry.name - val lineNumber = fileEntry.getLineNumber(offset) + 1 // since humans count from 1, not 0 - val columnNumber = fileEntry.getColumnNumber(offset) + 1 - // unsure whether should module name be added here - return IrMessageLogger.Location("$module @ $fileName", lineNumber, columnNumber) - } - - private fun IrDeclaration.reportUnlinkedSymbolsWarning(kind: String, fqn: FqName, localName: Name? = null) = - reportWarning( - buildString { - append(kind).append(" declaration ") - if (localName != null) append(localName.asString()).append(" at ") - append(fqn.asString()).append(" contains unlinked symbols") - }, - location() - ) - - private fun reportWarning(message: String, location: IrMessageLogger.Location?) { - messageLogger.report(IrMessageLogger.Severity.WARNING, message, location) - } - fun signatureTransformer(): IrElementTransformerVoid = SignatureTransformer() private inner class SignatureTransformer : IrElementTransformerVoid() { @@ -117,10 +89,8 @@ internal class UnlinkedDeclarationsProcessor( override fun visitField(declaration: IrField): IrStatement { if (declaration.type.isUnlinked()) { - val fqn = declaration.correspondingPropertySymbol?.owner?.fqNameWhenAvailable ?: declaration.fqNameForIrSerialization - val kind = if (declaration.correspondingPropertySymbol != null) "Property" else "Field" - declaration.reportUnlinkedSymbolsWarning(kind, fqn) - + // TODO: it would be more precise to use the set of unlinked symbols than a collection of unlinked types here. + declaration.logLinkageError(listOfNotNull((declaration.type as? IrSimpleType)?.classifier)) declaration.type = unlinkedMarkerTypeHandler.unlinkedMarkerType declaration.initializer = null } else { @@ -131,11 +101,8 @@ internal class UnlinkedDeclarationsProcessor( override fun visitVariable(declaration: IrVariable): IrStatement { if (declaration.type.isUnlinked()) { - val fqn = declaration.parent.fqNameForIrSerialization - val localName = declaration.name - val kind = if (declaration.isVar) "var" else "val" - declaration.reportUnlinkedSymbolsWarning(kind, fqn, localName) - + // TODO: it would be more precise to use the set of unlinked symbols than a collection of unlinked types here. + declaration.logLinkageError(listOfNotNull((declaration.type as? IrSimpleType)?.classifier)) declaration.type = unlinkedMarkerTypeHandler.unlinkedMarkerType declaration.initializer = null } else { @@ -199,37 +166,23 @@ internal class UnlinkedDeclarationsProcessor( if (!isImplementedFakeOverride && removedUnlinkedTypes.isEmpty()) { transformChildrenVoid() } else { - val functionKind = when { - this@transformBodyIfNecessary is IrSimpleFunction && correspondingPropertySymbol != null -> "property accessor" - this@transformBodyIfNecessary is IrConstructor -> "constructor" - else -> "function" - } - val errorMessages = listOfNotNull( if (isImplementedFakeOverride) buildString { - append("Abstract ").append(functionKind).append(" ") - append(fqNameForIrSerialization.asString()) - append(" is not implemented in non-abstract class ") - append(parent.fqNameForIrSerialization.asString()) + append("Abstract ").appendDeclaration(this@transformBodyIfNecessary) + append(" is not implemented in non-abstract ").appendDeclaration(parentAsClass) } else null, - if (removedUnlinkedTypes.isNotEmpty()) - buildString { - append("The signature of ").append(functionKind).append(" ") - append(fqNameForIrSerialization.asString()) - append(" contains unlinked symbols: ") - removedUnlinkedTypes.joinTo(this) { it.render() } - } - else null + if (removedUnlinkedTypes.isNotEmpty()) { + // TODO: it would be more precise to use the set of unlinked symbols than a collection of unlinked types here. + composeUnlinkedSymbolsErrorMessage(removedUnlinkedTypes.mapNotNull { (it as? IrSimpleType)?.classifier }) + } else null ) - errorMessages.forEach { reportWarning(it, location()) } - body?.let { body -> val bb = body as IrBlockBody bb.statements.clear() - bb.statements.add(throwLinkageError(unlinkedSymbol = null, errorMessages.joinToString(separator = "; "))) + bb.statements += throwLinkageError(errorMessages, location()) } } @@ -240,7 +193,7 @@ internal class UnlinkedDeclarationsProcessor( private fun IrSymbol.isUnlinked(): Boolean { if (!isBound) return true when (this) { - is IrClassifierSymbol -> this.isUnlinked() + is IrClassifierSymbol -> isUnlinked() is IrPropertySymbol -> { owner.getter?.let { if (it.symbol.isUnlinked()) return true } owner.setter?.let { if (it.symbol.isUnlinked()) return true } @@ -280,14 +233,36 @@ internal class UnlinkedDeclarationsProcessor( return with(unlinkedMarkerTypeHandler) { isUnlinkedMarkerType() } } - private fun IrElement.throwLinkageError(unlinkedSymbol: IrSymbol?, message: String = "Unlinked IR symbol"): IrCall { - val messageLiteral = message + unlinkedSymbol?.signature?.render()?.let { " $it" }.orEmpty() + private fun IrElement.composeUnlinkedSymbolsErrorMessage(unlinkedSymbols: Collection) = + renderError(this@composeUnlinkedSymbolsErrorMessage, unlinkedSymbols) + + private fun IrDeclaration.throwLinkageError(unlinkedSymbols: Collection = emptyList()): IrCall = + throwLinkageError( + messages = listOf(composeUnlinkedSymbolsErrorMessage(unlinkedSymbols)), + location = location() + ) + + private fun IrElement.throwLinkageError(messages: List, location: Location?): IrCall { + check(messages.isNotEmpty()) + + messages.forEach { logLinkageError(it, location) } val irCall = IrCallImpl(startOffset, endOffset, builtIns.nothingType, builtIns.linkageErrorSymbol, 0, 1, ERROR_ORIGIN) - irCall.putValueArgument(0, IrConstImpl.string(startOffset, endOffset, builtIns.stringType, messageLiteral)) + irCall.putValueArgument(0, IrConstImpl.string(startOffset, endOffset, builtIns.stringType, messages.joinToString("\n"))) return irCall } + private fun IrDeclaration.logLinkageError(unlinkedSymbols: Collection) { + logLinkageError( + composeUnlinkedSymbolsErrorMessage(unlinkedSymbols), + location() + ) + } + + private fun logLinkageError(message: String, location: Location?) { + messageLogger.report(Severity.WARNING, message, location) // It's OK. We log it as a warning. + } + fun usageTransformer(): IrElementTransformerVoid = UsageTransformer() private inner class UsageTransformer : IrElementTransformerVoid() { @@ -302,118 +277,106 @@ internal class UnlinkedDeclarationsProcessor( override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression { expression.transformChildrenVoid() - val checkType = expression.typeOperandClassifier - - if (!checkType.isUnlinked()) return expression - - reportWarning( - "TypeOperator contains unlinked symbol ${checkType.signature?.render() ?: ""}", - currentFile?.location(expression.startOffset) - ) - - val composite = IrCompositeImpl(expression.startOffset, expression.endOffset, builtIns.nothingType) - - composite.statements.add(expression.argument) - composite.statements.add(expression.throwLinkageError(expression.typeOperandClassifier)) - - return composite + val classifierSymbol = expression.typeOperandClassifier + return if (classifierSymbol.isUnlinked()) + IrCompositeImpl(expression.startOffset, expression.endOffset, builtIns.nothingType).apply { + statements += expression.argument + statements += expression.throwLinkageError(classifierSymbol) + } + else + expression } override fun visitExpression(expression: IrExpression): IrExpression { - if (expression.type.isUnlinked() || expression.type.isUnlinkedMarkerType()) { - reportWarning("Expression type contains unlinked symbol", currentFile?.location(expression.startOffset)) - return expression.throwLinkageError(unlinkedSymbol = null, "Unlinked type of IR expression") - } - return super.visitExpression(expression) + return if (expression.type.isUnlinked() || expression.type.isUnlinkedMarkerType()) + expression.throwLinkageError() // TODO: which exactly classifiers are unlinked? + else + super.visitExpression(expression) } override fun visitMemberAccess(expression: IrMemberAccessExpression<*>): IrExpression { expression.transformChildrenVoid() - val symbol = expression.symbol - if (!symbol.isUnlinked() && !expression.type.isUnlinked()) return expression + return if (!expression.symbol.isUnlinked() && !expression.type.isUnlinked()) + expression + else + IrCompositeImpl(expression.startOffset, expression.endOffset, builtIns.nothingType, expression.origin).apply { + statements.addIfNotNull(expression.dispatchReceiver) + statements.addIfNotNull(expression.extensionReceiver) - reportWarning( - "Accessing declaration with unlinked symbol ${symbol.signature?.render() ?: ""}", - currentFile?.location(expression.startOffset) - ) + for (i in 0 until expression.valueArgumentsCount) { + statements.addIfNotNull(expression.getValueArgument(i)) + } - val composite = IrCompositeImpl(expression.startOffset, expression.endOffset, builtIns.nothingType, expression.origin) - - expression.dispatchReceiver?.let { composite.statements.add(it) } - expression.extensionReceiver?.let { composite.statements.add(it) } - - for (i in 0 until expression.valueArgumentsCount) { - val arg = expression.getValueArgument(i) - arg?.let { composite.statements.add(it) } - } - - composite.statements.add( - if (!symbol.isBound) - expression.throwLinkageError(symbol) - else - expression.throwLinkageError(symbol, "Unlinked type in signature of IR symbol") - ) - - return composite + statements += expression.throwLinkageError() // TODO: which exactly classifiers are unlinked? + } } override fun visitFieldAccess(expression: IrFieldAccessExpression): IrExpression { expression.transformChildrenVoid() - val symbol = expression.symbol - if (!symbol.isUnlinked()) { - return expression - } - reportWarning( - "Accessing field contains unlinked symbol ${symbol.signature?.render()}", - currentFile?.location(expression.startOffset) - ) - - val composite = IrCompositeImpl(expression.startOffset, expression.endOffset, builtIns.nothingType, expression.origin) - expression.receiver?.let { composite.statements.add(it) } - if (expression is IrSetField) { - composite.statements.add(expression.value) - } - composite.statements.add( - if (!symbol.isBound) - expression.throwLinkageError(symbol) - else - expression.throwLinkageError(symbol, "Field type is unlinked in IR symbol") - ) - return composite + return if (!expression.symbol.isUnlinked()) + expression + else + IrCompositeImpl(expression.startOffset, expression.endOffset, builtIns.nothingType, expression.origin).apply { + statements.addIfNotNull(expression.receiver) + if (expression is IrSetField) + statements += expression.value + statements += expression.throwLinkageError(expression.symbol) + } } override fun visitClassReference(expression: IrClassReference): IrExpression { - if (expression.symbol.isUnlinked()) { - reportWarning( - "Accessing class contains unlinked symbol ${expression.symbol.signature?.render()}", - currentFile?.location(expression.startOffset) - ) - return expression.throwLinkageError(expression.symbol) - } - return expression + return if (expression.symbol.isUnlinked()) + expression.throwLinkageError(expression.symbol) + else + expression } override fun visitClass(declaration: IrClass): IrStatement { declaration.transformChildrenVoid() - fun IrOverridableDeclaration.filterOverriddens() { + fun IrOverridableDeclaration.filterOverriddenSymbols() { overriddenSymbols = overriddenSymbols.filter { it.isBound } } for (member in declaration.declarations) { when (member) { - is IrSimpleFunction -> member.filterOverriddens() + is IrSimpleFunction -> member.filterOverriddenSymbols() is IrProperty -> { - member.filterOverriddens() - member.getter?.filterOverriddens() - member.setter?.filterOverriddens() + member.filterOverriddenSymbols() + member.getter?.filterOverriddenSymbols() + member.setter?.filterOverriddenSymbols() } } } return declaration } + + private fun IrExpression.throwLinkageError(unlinkedSymbol: IrSymbol? = null): IrCall = + throwLinkageError( + messages = listOf(composeUnlinkedSymbolsErrorMessage(listOfNotNull(unlinkedSymbol))), + location = locationIn(currentFile) + ) } -} \ No newline at end of file + + companion object { + private val ERROR_ORIGIN = object : IrStatementOriginImpl("LINKAGE ERROR") {} + } +} + +private fun IrDeclaration.location(): Location? = locationIn(fileOrNull) + +private fun IrElement.locationIn(currentFile: IrFile?): Location? { + if (currentFile == null) return null + + val moduleName = currentFile.module.name + val fileEntry = currentFile.fileEntry + val fileName = fileEntry.name + val lineNumber = fileEntry.getLineNumber(startOffset) + 1 // since humans count from 1, not 0 + val columnNumber = fileEntry.getColumnNumber(startOffset) + 1 + + // unsure whether should module name be added here + return Location("$moduleName @ $fileName", lineNumber, columnNumber) +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedIrElementRenderer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedIrElementRenderer.kt new file mode 100644 index 00000000000..66eec7243a2 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/unlinked/UnlinkedIrElementRenderer.kt @@ -0,0 +1,221 @@ +/* + * 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.backend.common.serialization.unlinked + +import org.jetbrains.kotlin.backend.common.serialization.unlinked.DeclarationKind.* +import org.jetbrains.kotlin.backend.common.serialization.unlinked.ExpressionKind.* +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.util.IdSignature +import org.jetbrains.kotlin.ir.util.IdSignature.* +import org.jetbrains.kotlin.ir.util.isAnonymousObject +import org.jetbrains.kotlin.ir.util.nameForIrSerialization + +// TODO: Consider getting rid of this class when new self-descriptive signatures are implemented. +internal object UnlinkedIrElementRenderer { + fun StringBuilder.appendDeclaration(declaration: IrDeclaration): StringBuilder = appendDeclaration( + declarationKind = declaration.declarationKind, + declarationName = declaration.symbol.guessName() + ) + + private fun StringBuilder.appendDeclaration(declarationKind: DeclarationKind, declarationName: String?): StringBuilder { + append(declarationKind) + + if (declarationKind != ANONYMOUS_OBJECT) { + // This is a declaration NOT under a property. + appendWithWhitespaceBefore(declarationName ?: UNKNOWN_NAME) + } + + return this + } + + fun renderError(element: IrElement, unlinkedSymbols: Collection): String = buildString { + when (element) { + is IrDeclaration -> appendDeclaration(element) + is IrExpression -> { + val (expressionKind, referencedDeclarationKind) = element.expression + appendWithWhitespaceAfter(expressionKind.displayName) + + if (referencedDeclarationKind != null) { + val referencedDeclarationSymbol = when (element) { + is IrDeclarationReference -> element.symbol + is IrInstanceInitializerCall -> element.classSymbol + else -> null + } + val referencedDeclaration = referencedDeclarationSymbol?.boundOwnerDeclarationOrNull + if (referencedDeclaration == null) { + // If it's impossible to obtain referenced declaration because the symbol of this declaration is unbound, + // but the declaration itself is supposed to be, then do best effort and try to output the short name of + // the declaration at least. + appendDeclaration(referencedDeclarationKind, referencedDeclarationSymbol?.guessName()) + } else { + appendDeclaration(referencedDeclaration) + } + } + + append(" can not be ").append(expressionKind.verb3rdForm).append(" because it") + } + else -> error("Unexpected type of IR element: ${this::class.java}, $this") + } + + append(" uses unlinked symbols") + if (unlinkedSymbols.isNotEmpty()) { + unlinkedSymbols.joinTo(this, prefix = ": ") { it.anySignature?.render() ?: UNKNOWN_SYMBOL } + } + } +} + +private enum class DeclarationKind(private val displayName: String) { + CLASS("class"), + INTERFACE("interface"), + ENUM_CLASS("enum class"), + ENUM_ENTRY("enum entry"), + ANNOTATION_CLASS("annotation class"), + OBJECT("object"), + ANONYMOUS_OBJECT("anonymous object"), + COMPANION_OBJECT("companion object"), + MUTABLE_VARIABLE("var"), + IMMUTABLE_VARIABLE("val"), + VALUE_PARAMETER("value parameter"), + FIELD("field"), + FIELD_OF_PROPERTY("backing field of property"), + PROPERTY("property"), + PROPERTY_ACCESSOR("property accessor"), + FUNCTION("function"), + CONSTRUCTOR("constructor"), + OTHER_DECLARATION("declaration"); + + override fun toString() = displayName +} + +private val IrDeclaration.declarationKind: DeclarationKind + get() = when (this) { + is IrClass -> when (kind) { + ClassKind.CLASS -> if (isAnonymousObject) ANONYMOUS_OBJECT else CLASS + ClassKind.INTERFACE -> INTERFACE + ClassKind.ENUM_CLASS -> ENUM_CLASS + ClassKind.ENUM_ENTRY -> ENUM_ENTRY + ClassKind.ANNOTATION_CLASS -> ANNOTATION_CLASS + ClassKind.OBJECT -> if (isCompanion) COMPANION_OBJECT else OBJECT + } + is IrVariable -> if (isVar) MUTABLE_VARIABLE else IMMUTABLE_VARIABLE + is IrValueParameter -> VALUE_PARAMETER + is IrField -> if (correspondingPropertySymbol != null) FIELD_OF_PROPERTY else FIELD + is IrProperty -> PROPERTY + is IrSimpleFunction -> if (correspondingPropertySymbol != null) PROPERTY_ACCESSOR else FUNCTION + is IrConstructor -> CONSTRUCTOR + else -> OTHER_DECLARATION + } + +private val IrFunctionSymbol.functionDeclarationKind: DeclarationKind + get() = when (this) { + is IrConstructorSymbol -> CONSTRUCTOR + is IrSimpleFunctionSymbol -> boundOwnerDeclarationOrNull?.declarationKind + ?: if (anySignature is AccessorSignature) PROPERTY_ACCESSOR else FUNCTION + else -> OTHER_DECLARATION // Something unexpected. + } + +private val IrFieldSymbol.fieldDeclarationKind: DeclarationKind + get() = boundOwnerDeclarationOrNull?.declarationKind + ?: FIELD // Fallback to simple field as it's impossible to make a guess by signature. + +private val IrValueSymbol.valueDeclarationKind: DeclarationKind + get() = when (this) { + is IrValueParameterSymbol -> VALUE_PARAMETER + is IrVariableSymbol -> boundOwnerDeclarationOrNull?.declarationKind + ?: IMMUTABLE_VARIABLE // Fallback to immutable variable as it's impossible to make a guess by signature. + else -> OTHER_DECLARATION // Something unexpected. + } + +private val IrSymbol.classDeclarationKind: DeclarationKind + get() = when (this) { + is IrClassSymbol -> boundOwnerDeclarationOrNull?.declarationKind + ?: CLASS // Fallback to class as it's impossible to make a guess by signature. + is IrEnumEntrySymbol -> ENUM_ENTRY + else -> OTHER_DECLARATION // Something unexpected. + } + +private data class Expression(val kind: ExpressionKind, val referencedDeclarationKind: DeclarationKind?) + +private enum class ExpressionKind(val displayName: String?, val verb3rdForm: String) { + REFERENCE("reference to", "evaluated"), + CALLING(null, "called"), + CALLING_INSTANCE_INITIALIZER("instance initializer of", "called"), + READING(null, "read"), + WRITING(null, "written"), + GETTING_INSTANCE(null, "gotten"), + OTHER_EXPRESSION("expression", "evaluated") +} + +// More can be added for verbosity in the future. +private val IrExpression.expression: Expression + get() = when (this) { + is IrDeclarationReference -> when (this) { + is IrFunctionReference -> Expression(REFERENCE, symbol.functionDeclarationKind) + is IrPropertyReference, + is IrLocalDelegatedPropertyReference -> Expression(REFERENCE, PROPERTY) + is IrCall -> Expression(CALLING, symbol.functionDeclarationKind) + is IrConstructorCall, + is IrEnumConstructorCall, + is IrDelegatingConstructorCall -> Expression(CALLING, CONSTRUCTOR) + is IrClassReference -> Expression(REFERENCE, symbol.classDeclarationKind) + is IrGetField -> Expression(READING, symbol.fieldDeclarationKind) + is IrSetField -> Expression(WRITING, symbol.fieldDeclarationKind) + is IrGetValue -> Expression(READING, symbol.valueDeclarationKind) + is IrSetValue -> Expression(WRITING, symbol.valueDeclarationKind) + is IrGetSingletonValue -> Expression(GETTING_INSTANCE, symbol.classDeclarationKind) + else -> Expression(REFERENCE, OTHER_DECLARATION) + } + is IrInstanceInitializerCall -> Expression(CALLING_INSTANCE_INITIALIZER, classSymbol.classDeclarationKind) + else -> Expression(OTHER_EXPRESSION, null) + } + +private val IrSymbol.boundOwnerDeclarationOrNull: IrDeclaration? + get() = if (isBound) owner as? IrDeclaration else null + +private fun IrSymbol.guessName(): String? { + fun IdSignature.guessNameBySignature(nameSegmentsToPickUp: Int): String? = when (this) { + is CommonSignature -> nameSegments.takeLast(nameSegmentsToPickUp).joinToString(".") + is CompositeSignature -> inner.guessNameBySignature(nameSegmentsToPickUp) + is AccessorSignature -> accessorSignature.guessNameBySignature(nameSegmentsToPickUp) + else -> null + } + + return anySignature + ?.let { effectiveSignature -> + val nameSegmentsToPickUp = when { + effectiveSignature is AccessorSignature -> 2 // property_name.accessor_name + this is IrConstructorSymbol -> 2 // class_name. + else -> 1 + } + effectiveSignature.guessNameBySignature(nameSegmentsToPickUp) + } + ?: boundOwnerDeclarationOrNull?.nameForIrSerialization?.asString() +} + +private val IrSymbol.anySignature: IdSignature? + get() = signature ?: privateSignature + +private const val UNKNOWN_SYMBOL = "" +private const val UNKNOWN_NAME = "" + +private fun StringBuilder.appendWithWhitespaceBefore(text: String): StringBuilder { + if (text.isNotEmpty()) appendWhitespaceIfNotEmpty().append(text) + return this +} + +private fun StringBuilder.appendWithWhitespaceAfter(text: String?): StringBuilder { + if (!text.isNullOrEmpty()) append(text).append(" ") + return this +} + +private fun StringBuilder.appendWhitespaceIfNotEmpty(): StringBuilder { + if (isNotEmpty()) append(" ") + return this +}