[IR] Partial linkage: report the exact IR element where unlinked symbols are used
This commit is contained in:
+110
-147
@@ -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<IrSymbol>) =
|
||||
renderError(this@composeUnlinkedSymbolsErrorMessage, unlinkedSymbols)
|
||||
|
||||
private fun IrDeclaration.throwLinkageError(unlinkedSymbols: Collection<IrSymbol> = emptyList()): IrCall =
|
||||
throwLinkageError(
|
||||
messages = listOf(composeUnlinkedSymbolsErrorMessage(unlinkedSymbols)),
|
||||
location = location()
|
||||
)
|
||||
|
||||
private fun IrElement.throwLinkageError(messages: List<String>, 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<IrSymbol>) {
|
||||
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 <S : IrSymbol> IrOverridableDeclaration<S>.filterOverriddens() {
|
||||
fun <S : IrSymbol> IrOverridableDeclaration<S>.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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
+221
@@ -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<IrSymbol>): 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.<init>
|
||||
else -> 1
|
||||
}
|
||||
effectiveSignature.guessNameBySignature(nameSegmentsToPickUp)
|
||||
}
|
||||
?: boundOwnerDeclarationOrNull?.nameForIrSerialization?.asString()
|
||||
}
|
||||
|
||||
private val IrSymbol.anySignature: IdSignature?
|
||||
get() = signature ?: privateSignature
|
||||
|
||||
private const val UNKNOWN_SYMBOL = "<unknown symbol>"
|
||||
private const val UNKNOWN_NAME = "<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
|
||||
}
|
||||
Reference in New Issue
Block a user