[IR] User-friendly message about unexpected unlinked symbols
^KT-53649
This commit is contained in:
+4
-4
@@ -51,7 +51,7 @@ abstract class KotlinIrLinker(
|
||||
|
||||
private lateinit var linkerExtensions: Collection<IrDeserializer.IrLinkerExtension>
|
||||
|
||||
protected open val unlinkedDeclarationsSupport: UnlinkedDeclarationsSupport get() = UnlinkedDeclarationsSupport.DISABLED
|
||||
open val unlinkedDeclarationsSupport: UnlinkedDeclarationsSupport get() = UnlinkedDeclarationsSupport.DISABLED
|
||||
protected open val userVisibleIrModulesSupport: UserVisibleIrModulesSupport get() = UserVisibleIrModulesSupport.DEFAULT
|
||||
|
||||
fun deserializeOrReturnUnboundIrSymbolIfPartialLinkageEnabled(
|
||||
@@ -75,7 +75,7 @@ abstract class KotlinIrLinker(
|
||||
if (unlinkedDeclarationsSupport.allowUnboundSymbols)
|
||||
referenceDeserializedSymbol(symbolTable, null, symbolKind, idSignature)
|
||||
else
|
||||
throw SignatureIdNotFoundInModuleWithDependencies(
|
||||
SignatureIdNotFoundInModuleWithDependencies(
|
||||
idSignature = idSignature,
|
||||
problemModuleDeserializer = moduleDeserializer,
|
||||
allModuleDeserializers = deserializersForModules.values,
|
||||
@@ -86,7 +86,7 @@ abstract class KotlinIrLinker(
|
||||
|
||||
fun resolveModuleDeserializer(module: ModuleDescriptor, idSignature: IdSignature?): IrModuleDeserializer {
|
||||
return deserializersForModules[module.name.asString()]
|
||||
?: throw NoDeserializerForModule(module.name, idSignature).raiseIssue(messageLogger)
|
||||
?: NoDeserializerForModule(module.name, idSignature).raiseIssue(messageLogger)
|
||||
}
|
||||
|
||||
protected abstract fun createModuleDeserializer(
|
||||
@@ -165,7 +165,7 @@ abstract class KotlinIrLinker(
|
||||
?: return null
|
||||
} catch (e: IrSymbolTypeMismatchException) {
|
||||
if (!unlinkedDeclarationsSupport.allowUnboundSymbols) {
|
||||
throw SymbolTypeMismatch(e, deserializersForModules.values, userVisibleIrModulesSupport).raiseIssue(messageLogger)
|
||||
SymbolTypeMismatch(e, deserializersForModules.values, userVisibleIrModulesSupport).raiseIssue(messageLogger)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-19
@@ -7,8 +7,6 @@ package org.jetbrains.kotlin.backend.common.serialization.linkerissues
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
sealed class IrDeserializationException(message: String) : Exception(message) {
|
||||
override val message: String get() = super.message!!
|
||||
@@ -22,20 +20,3 @@ class IrSymbolTypeMismatchException(
|
||||
class IrDisallowedErrorNode(
|
||||
clazz: Class<out IrAnnotationContainer>
|
||||
) : IrDeserializationException("${clazz::class.java.simpleName} found but error nodes are not allowed.")
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
internal inline fun <reified T : IrSymbol> checkSymbolType(symbol: IrSymbol): T {
|
||||
contract {
|
||||
returns() implies (symbol is T)
|
||||
}
|
||||
|
||||
if (symbol !is T) throw IrSymbolTypeMismatchException(T::class.java, symbol) else return symbol
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
internal inline fun <reified T : IrAnnotationContainer> checkErrorNodesAllowed(errorNodesAllowed: Boolean) {
|
||||
contract {
|
||||
returns() implies errorNodesAllowed
|
||||
}
|
||||
if (!errorNodesAllowed) throw IrDisallowedErrorNode(T::class.java)
|
||||
}
|
||||
|
||||
+46
-6
@@ -5,11 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.common.serialization.linkerissues
|
||||
|
||||
import org.jetbrains.kotlin.analyzer.CompilationErrorException
|
||||
import org.jetbrains.kotlin.backend.common.serialization.IrModuleDeserializer
|
||||
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.PotentialConflictKind.*
|
||||
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.PotentialConflictKind.Companion.mostSignificantConflictKind
|
||||
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.PotentialConflictReason.Companion.mostSignificantConflictReasons
|
||||
import org.jetbrains.kotlin.ir.linkage.KotlinIrLinkerInternalException
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.util.IdSignature
|
||||
import org.jetbrains.kotlin.ir.util.IrMessageLogger
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -18,12 +20,50 @@ import org.jetbrains.kotlin.utils.ResolvedDependencyId
|
||||
import org.jetbrains.kotlin.utils.ResolvedDependencyVersion
|
||||
import kotlin.Comparator
|
||||
|
||||
abstract class KotlinIrLinkerIssue {
|
||||
/**
|
||||
* TODO: Currently, [KotlinIrLinkerInternalException] is only needed to show the stacktrace to the user.
|
||||
* If stacktrace is not needed it's enough to throw [CompilationErrorException] to interrupt the compilation process.
|
||||
* But, probably, we don't even need to show the stacktrace, so we could probably get rid of [KotlinIrLinkerInternalException] at all.
|
||||
*/
|
||||
abstract class KotlinIrLinkerIssue(private val needStacktrace: Boolean) {
|
||||
protected abstract val errorMessage: String
|
||||
|
||||
fun raiseIssue(messageLogger: IrMessageLogger): KotlinIrLinkerInternalException {
|
||||
fun raiseIssue(messageLogger: IrMessageLogger): Nothing {
|
||||
messageLogger.report(IrMessageLogger.Severity.ERROR, errorMessage, null)
|
||||
throw KotlinIrLinkerInternalException()
|
||||
throw if (needStacktrace) KotlinIrLinkerInternalException() else CompilationErrorException()
|
||||
}
|
||||
}
|
||||
|
||||
class UnexpectedUnboundIrSymbols(unboundSymbols: Set<IrSymbol>, whenDetected: String) : KotlinIrLinkerIssue(needStacktrace = false) {
|
||||
override val errorMessage = buildString {
|
||||
// cause:
|
||||
append("There ").append(
|
||||
when (val count = unboundSymbols.size) {
|
||||
1 -> "is still an unbound symbol"
|
||||
else -> "are still $count unbound symbols"
|
||||
}
|
||||
).append(" ").append(whenDetected).append(":\n")
|
||||
unboundSymbols.joinTo(this, separator = "\n")
|
||||
|
||||
// explanation:
|
||||
append("\n\nThis could happen if there are two libraries, where one library was compiled against the different version")
|
||||
append(" of the other library than the one currently used in the project.")
|
||||
|
||||
// action items:
|
||||
append(" Please check that the project configuration is correct and has consistent versions of dependencies.")
|
||||
if (unboundSymbols.any { looksLikeEnumEntries(it.signature) }) {
|
||||
append("\n\nAnother possible reason is that some parts of the project are compiled with EnumEntries language feature enabled,")
|
||||
append(" but other parts or used libraries are compiled with EnumEntries language feature disabled.")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun looksLikeEnumEntries(signature: IdSignature?) : Boolean = when (signature) {
|
||||
is IdSignature.AccessorSignature -> looksLikeEnumEntries(signature.propertySignature)
|
||||
is IdSignature.CompositeSignature -> looksLikeEnumEntries(signature.inner)
|
||||
is IdSignature.CommonSignature -> signature.shortName == "entries"
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +72,7 @@ class SignatureIdNotFoundInModuleWithDependencies(
|
||||
private val problemModuleDeserializer: IrModuleDeserializer,
|
||||
private val allModuleDeserializers: Collection<IrModuleDeserializer>,
|
||||
private val userVisibleIrModulesSupport: UserVisibleIrModulesSupport
|
||||
) : KotlinIrLinkerIssue() {
|
||||
) : KotlinIrLinkerIssue(needStacktrace = false) {
|
||||
override val errorMessage = try {
|
||||
computeErrorMessage()
|
||||
} catch (e: Throwable) {
|
||||
@@ -89,7 +129,7 @@ class SignatureIdNotFoundInModuleWithDependencies(
|
||||
}
|
||||
}
|
||||
|
||||
class NoDeserializerForModule(moduleName: Name, idSignature: IdSignature?) : KotlinIrLinkerIssue() {
|
||||
class NoDeserializerForModule(moduleName: Name, idSignature: IdSignature?) : KotlinIrLinkerIssue(needStacktrace = false) {
|
||||
override val errorMessage = buildString {
|
||||
append("Could not load module ${moduleName.asString()}")
|
||||
if (idSignature != null) append(" in an attempt to find deserializer for symbol ${idSignature.render()}.")
|
||||
@@ -100,7 +140,7 @@ class SymbolTypeMismatch(
|
||||
private val cause: IrSymbolTypeMismatchException,
|
||||
private val allModuleDeserializers: Collection<IrModuleDeserializer>,
|
||||
private val userVisibleIrModulesSupport: UserVisibleIrModulesSupport
|
||||
) : KotlinIrLinkerIssue() {
|
||||
) : KotlinIrLinkerIssue(needStacktrace = true) {
|
||||
override val errorMessage = try {
|
||||
computeErrorMessage()
|
||||
} catch (e: Throwable) {
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.linkerissues
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.serialization.KotlinIrLinker
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.util.IrMessageLogger
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.allUnbound
|
||||
import org.jetbrains.kotlin.ir.util.irMessageLogger
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
internal inline fun <reified T : IrSymbol> checkSymbolType(symbol: IrSymbol): T {
|
||||
contract {
|
||||
returns() implies (symbol is T)
|
||||
}
|
||||
|
||||
if (symbol !is T) throw IrSymbolTypeMismatchException(T::class.java, symbol) else return symbol
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
internal inline fun <reified T : IrAnnotationContainer> checkErrorNodesAllowed(errorNodesAllowed: Boolean) {
|
||||
contract {
|
||||
returns() implies errorNodesAllowed
|
||||
}
|
||||
if (!errorNodesAllowed) throw IrDisallowedErrorNode(T::class.java)
|
||||
}
|
||||
|
||||
// N.B. Checks for absence of unbound symbols only when unbound symbols are not allowed.
|
||||
fun KotlinIrLinker.checkNoUnboundSymbols(symbolTable: SymbolTable, whenDetected: String) {
|
||||
if (!unlinkedDeclarationsSupport.allowUnboundSymbols)
|
||||
messageLogger.checkNoUnboundSymbols(symbolTable, whenDetected)
|
||||
}
|
||||
|
||||
// N.B. Always checks for absence of unbound symbols. The condition whether this check should be applied is controlled outside.
|
||||
fun IrMessageLogger.checkNoUnboundSymbols(symbolTable: SymbolTable, whenDetected: String) {
|
||||
val unboundSymbols = symbolTable.allUnbound
|
||||
if (unboundSymbols.isNotEmpty())
|
||||
UnexpectedUnboundIrSymbols(unboundSymbols, whenDetected).raiseIssue(this)
|
||||
}
|
||||
|
||||
// N.B. Always checks for absence of unbound symbols. The condition whether this check should be applied is controlled outside.
|
||||
fun CompilerConfiguration.checkNoUnboundSymbols(symbolTable: SymbolTable, whenDetected: String) {
|
||||
val unboundSymbols = symbolTable.allUnbound
|
||||
if (unboundSymbols.isNotEmpty())
|
||||
UnexpectedUnboundIrSymbols(unboundSymbols, whenDetected).raiseIssue(irMessageLogger)
|
||||
}
|
||||
Reference in New Issue
Block a user