[JVM] Split JvmSignatureClashDetector into two classes
One for methods, one for fields. Later we are going to use the new `SignatureClashDetector` class for detecting clashes of KLIB signatures.
This commit is contained in:
committed by
Space Team
parent
e0f9e09a97
commit
e0cb145c6b
+5
-3
@@ -112,6 +112,7 @@ class ClassCodegen private constructor(
|
||||
val reifiedTypeParametersUsages = ReifiedTypeParametersUsages()
|
||||
|
||||
private val jvmMethodSignatureClashDetector = JvmMethodSignatureClashDetector(this)
|
||||
private val jvmFieldSignatureClashDetector = JvmFieldSignatureClashDetector(this)
|
||||
|
||||
private val visitor = state.factory.newVisitor(irClass.descriptorOrigin, type, irClass.fileParent.loadSourceFilesInfo()).apply {
|
||||
val signature = typeMapper.mapClassSignature(irClass, type, state.classBuilderMode.generateBodies)
|
||||
@@ -217,7 +218,8 @@ class ClassCodegen private constructor(
|
||||
generateInnerAndOuterClasses()
|
||||
|
||||
visitor.done(config.generateSmapCopyToAnnotation)
|
||||
jvmMethodSignatureClashDetector.reportErrors()
|
||||
jvmMethodSignatureClashDetector.reportErrorsTo(context.ktDiagnosticReporter)
|
||||
jvmFieldSignatureClashDetector.reportErrorsTo(context.ktDiagnosticReporter)
|
||||
}
|
||||
|
||||
private fun shouldSkipCodeGenerationAccordingToGenerationFilter(): Boolean {
|
||||
@@ -354,7 +356,7 @@ class ClassCodegen private constructor(
|
||||
fieldSignature, (field.initializer?.expression as? IrConst<*>)?.value
|
||||
)
|
||||
|
||||
jvmMethodSignatureClashDetector.trackField(field, RawSignature(fieldName, fieldType.descriptor, MemberKind.FIELD))
|
||||
jvmFieldSignatureClashDetector.trackDeclaration(field, RawSignature(fieldName, fieldType.descriptor, MemberKind.FIELD))
|
||||
|
||||
if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) {
|
||||
val skipNullabilityAnnotations =
|
||||
@@ -442,7 +444,7 @@ class ClassCodegen private constructor(
|
||||
} else {
|
||||
node.accept(smapCopyingVisitor)
|
||||
}
|
||||
jvmMethodSignatureClashDetector.trackMethod(method, RawSignature(node.name, node.desc, MemberKind.METHOD))
|
||||
jvmMethodSignatureClashDetector.trackDeclaration(method, RawSignature(node.name, node.desc, MemberKind.METHOD))
|
||||
|
||||
when (val metadata = method.metadata) {
|
||||
is MetadataSource.Property -> metadataSerializer.bindPropertyMetadata(metadata, Method(node.name, node.desc), method.origin)
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrDiagnosticReporter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.linkage.SignatureClashDetector
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ConflictingJvmDeclarationsData
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmBackendErrors
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.RawSignature
|
||||
|
||||
internal class JvmFieldSignatureClashDetector(
|
||||
private val classCodegen: ClassCodegen,
|
||||
) : SignatureClashDetector<RawSignature, IrField>() {
|
||||
|
||||
override fun reportSignatureConflict(
|
||||
signature: RawSignature,
|
||||
declarations: Collection<IrField>,
|
||||
diagnosticReporter: IrDiagnosticReporter
|
||||
) {
|
||||
reportSignatureClashTo(
|
||||
diagnosticReporter,
|
||||
JvmBackendErrors.CONFLICTING_JVM_DECLARATIONS,
|
||||
declarations,
|
||||
ConflictingJvmDeclarationsData(
|
||||
classInternalName = classCodegen.type.internalName,
|
||||
classOrigin = null,
|
||||
signature = signature,
|
||||
signatureOrigins = null,
|
||||
signatureDescriptors = declarations.map(IrDeclaration::toIrBasedDescriptor),
|
||||
),
|
||||
reportOnIfSynthetic = { classCodegen.irClass },
|
||||
)
|
||||
}
|
||||
}
|
||||
+64
-77
@@ -9,38 +9,30 @@ import org.jetbrains.kotlin.backend.common.lower.ANNOTATION_IMPLEMENTATION
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.IrDiagnosticReporter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.linkage.SignatureClashDetector
|
||||
import org.jetbrains.kotlin.ir.util.isFakeOverride
|
||||
import org.jetbrains.kotlin.ir.util.sourceElement
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ConflictingJvmDeclarationsData
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmBackendErrors
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MemberKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.RawSignature
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
|
||||
class JvmMethodSignatureClashDetector(
|
||||
private val classCodegen: ClassCodegen
|
||||
) {
|
||||
private val methodsBySignature = LinkedHashMap<RawSignature, MutableSet<IrFunction>>()
|
||||
private val fieldsBySignature = LinkedHashMap<RawSignature, MutableSet<IrField>>()
|
||||
|
||||
fun trackField(irField: IrField, rawSignature: RawSignature) {
|
||||
fieldsBySignature.getOrPut(rawSignature) { SmartSet.create() }.add(irField)
|
||||
}
|
||||
|
||||
fun trackMethod(irFunction: IrFunction, rawSignature: RawSignature) {
|
||||
methodsBySignature.getOrPut(rawSignature) { SmartSet.create() }.add(irFunction)
|
||||
}
|
||||
) : SignatureClashDetector<RawSignature, IrFunction>() {
|
||||
|
||||
fun trackFakeOverrideMethod(irFunction: IrFunction) {
|
||||
if (irFunction.dispatchReceiverParameter != null) {
|
||||
for (overriddenFunction in getOverriddenFunctions(irFunction as IrSimpleFunction)) {
|
||||
if (!overriddenFunction.isFakeOverride) trackMethod(irFunction, mapRawSignature(overriddenFunction))
|
||||
if (!overriddenFunction.isFakeOverride) trackDeclaration(irFunction, mapRawSignature(overriddenFunction))
|
||||
}
|
||||
} else {
|
||||
trackMethod(irFunction, mapRawSignature(irFunction))
|
||||
trackDeclaration(irFunction, mapRawSignature(irFunction))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,90 +61,85 @@ class JvmMethodSignatureClashDetector(
|
||||
private fun IrFunction.isSpecialOverride(): Boolean =
|
||||
origin in SPECIAL_BRIDGES_AND_OVERRIDES
|
||||
|
||||
fun reportErrors() {
|
||||
reportMethodSignatureConflicts()
|
||||
reportPredefinedMethodSignatureConflicts()
|
||||
reportFieldSignatureConflicts()
|
||||
override fun reportErrorsTo(diagnosticReporter: IrDiagnosticReporter) {
|
||||
super.reportErrorsTo(diagnosticReporter)
|
||||
reportPredefinedMethodSignatureConflicts(diagnosticReporter)
|
||||
}
|
||||
|
||||
private fun reportMethodSignatureConflicts() {
|
||||
for ((rawSignature, methods) in methodsBySignature) {
|
||||
if (methods.size <= 1) continue
|
||||
override fun reportSignatureConflict(
|
||||
signature: RawSignature,
|
||||
declarations: Collection<IrFunction>,
|
||||
diagnosticReporter: IrDiagnosticReporter
|
||||
) {
|
||||
val fakeOverridesCount = declarations.count { it.isFakeOverride }
|
||||
val specialOverridesCount = declarations.count { it.isSpecialOverride() }
|
||||
val realMethodsCount = declarations.size - fakeOverridesCount - specialOverridesCount
|
||||
|
||||
val fakeOverridesCount = methods.count { it.isFakeOverride }
|
||||
val specialOverridesCount = methods.count { it.isSpecialOverride() }
|
||||
val realMethodsCount = methods.size - fakeOverridesCount - specialOverridesCount
|
||||
val conflictingJvmDeclarationsData = getConflictingJvmDeclarationsData(signature, declarations)
|
||||
|
||||
val conflictingJvmDeclarationsData = getConflictingJvmDeclarationsData(rawSignature, methods)
|
||||
|
||||
when {
|
||||
realMethodsCount == 0 && (fakeOverridesCount > 1 || specialOverridesCount > 1) ->
|
||||
if (classCodegen.irClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) {
|
||||
reportJvmSignatureClash(
|
||||
JvmBackendErrors.CONFLICTING_INHERITED_JVM_DECLARATIONS,
|
||||
listOf(classCodegen.irClass),
|
||||
conflictingJvmDeclarationsData
|
||||
)
|
||||
}
|
||||
|
||||
fakeOverridesCount == 0 && specialOverridesCount == 0 -> {
|
||||
// In IFoo$DefaultImpls we should report errors only if there are private methods among conflicting ones
|
||||
// (otherwise such errors would be reported twice: once for IFoo and once for IFoo$DefaultImpls).
|
||||
if (classCodegen.irClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS ||
|
||||
methods.any { DescriptorVisibilities.isPrivate(it.visibility) }
|
||||
) {
|
||||
reportJvmSignatureClash(
|
||||
JvmBackendErrors.CONFLICTING_JVM_DECLARATIONS,
|
||||
methods,
|
||||
conflictingJvmDeclarationsData
|
||||
)
|
||||
}
|
||||
when {
|
||||
realMethodsCount == 0 && (fakeOverridesCount > 1 || specialOverridesCount > 1) ->
|
||||
if (classCodegen.irClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) {
|
||||
reportJvmSignatureClash(
|
||||
diagnosticReporter,
|
||||
JvmBackendErrors.CONFLICTING_INHERITED_JVM_DECLARATIONS,
|
||||
listOf(classCodegen.irClass),
|
||||
conflictingJvmDeclarationsData
|
||||
)
|
||||
}
|
||||
|
||||
else ->
|
||||
if (classCodegen.irClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) {
|
||||
reportJvmSignatureClash(
|
||||
JvmBackendErrors.ACCIDENTAL_OVERRIDE,
|
||||
methods.filter { !it.isFakeOverride && !it.isSpecialOverride() },
|
||||
conflictingJvmDeclarationsData
|
||||
)
|
||||
}
|
||||
fakeOverridesCount == 0 && specialOverridesCount == 0 -> {
|
||||
// In IFoo$DefaultImpls we should report errors only if there are private methods among conflicting ones
|
||||
// (otherwise such errors would be reported twice: once for IFoo and once for IFoo$DefaultImpls).
|
||||
if (classCodegen.irClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS ||
|
||||
declarations.any { DescriptorVisibilities.isPrivate(it.visibility) }
|
||||
) {
|
||||
reportJvmSignatureClash(
|
||||
diagnosticReporter,
|
||||
JvmBackendErrors.CONFLICTING_JVM_DECLARATIONS,
|
||||
declarations,
|
||||
conflictingJvmDeclarationsData
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
if (classCodegen.irClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) {
|
||||
reportJvmSignatureClash(
|
||||
diagnosticReporter,
|
||||
JvmBackendErrors.ACCIDENTAL_OVERRIDE,
|
||||
declarations.filter { !it.isFakeOverride && !it.isSpecialOverride() },
|
||||
conflictingJvmDeclarationsData
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportPredefinedMethodSignatureConflicts() {
|
||||
private fun reportPredefinedMethodSignatureConflicts(diagnosticReporter: IrDiagnosticReporter) {
|
||||
for (predefinedSignature in PREDEFINED_SIGNATURES) {
|
||||
val knownMethods = methodsBySignature[predefinedSignature] ?: continue
|
||||
val methods = knownMethods.filter { !it.isFakeOverride && !it.isSpecialOverride() }
|
||||
val methods = declarationsWithSignature(predefinedSignature).filter { !it.isFakeOverride && !it.isSpecialOverride() }
|
||||
if (methods.isEmpty()) continue
|
||||
val conflictingJvmDeclarationsData = ConflictingJvmDeclarationsData(
|
||||
classCodegen.type.internalName, null, predefinedSignature, null, methods.map(IrFunction::toIrBasedDescriptor),
|
||||
)
|
||||
reportJvmSignatureClash(JvmBackendErrors.ACCIDENTAL_OVERRIDE, methods, conflictingJvmDeclarationsData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportFieldSignatureConflicts() {
|
||||
for ((rawSignature, fields) in fieldsBySignature) {
|
||||
if (fields.size <= 1) continue
|
||||
val conflictingJvmDeclarationsData = getConflictingJvmDeclarationsData(rawSignature, fields)
|
||||
reportJvmSignatureClash(JvmBackendErrors.CONFLICTING_JVM_DECLARATIONS, fields, conflictingJvmDeclarationsData)
|
||||
reportJvmSignatureClash(diagnosticReporter, JvmBackendErrors.ACCIDENTAL_OVERRIDE, methods, conflictingJvmDeclarationsData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportJvmSignatureClash(
|
||||
diagnosticReporter: IrDiagnosticReporter,
|
||||
diagnosticFactory1: KtDiagnosticFactory1<ConflictingJvmDeclarationsData>,
|
||||
irDeclarations: Collection<IrDeclaration>,
|
||||
conflictingJvmDeclarationsData: ConflictingJvmDeclarationsData
|
||||
) {
|
||||
irDeclarations.mapTo(LinkedHashSet()) { irDeclaration ->
|
||||
reportSignatureClashTo(
|
||||
diagnosticReporter,
|
||||
diagnosticFactory1,
|
||||
irDeclarations,
|
||||
conflictingJvmDeclarationsData,
|
||||
// Offset can be negative (SYNTHETIC_OFFSET) for delegated members; report an error on the class in that case.
|
||||
val reportOn = irDeclaration.takeUnless { it.startOffset < 0 } ?: classCodegen.irClass
|
||||
classCodegen.context.ktDiagnosticReporter.at(reportOn.sourceElement(), reportOn, irDeclaration.file)
|
||||
}.forEach {
|
||||
it.report(diagnosticFactory1, conflictingJvmDeclarationsData)
|
||||
}
|
||||
reportOnIfSynthetic = { classCodegen.irClass },
|
||||
)
|
||||
}
|
||||
|
||||
private fun getConflictingJvmDeclarationsData(
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.ir.linkage
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1
|
||||
import org.jetbrains.kotlin.ir.IrDiagnosticReporter
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.sourceElement
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
|
||||
/**
|
||||
* Collects the information about declarations and their signatures and reports cases when multiple declarations have the same signature.
|
||||
*/
|
||||
abstract class SignatureClashDetector<Signature : Any, Declaration : IrDeclaration> {
|
||||
private val declarationsBySignature: HashMap<Signature, MutableSet<Declaration>> = LinkedHashMap()
|
||||
|
||||
/**
|
||||
* Returns all the declarations that have [signature] previously recorded by [trackDeclaration].
|
||||
*/
|
||||
protected fun declarationsWithSignature(signature: Signature): Set<Declaration> =
|
||||
declarationsBySignature[signature] ?: emptySet()
|
||||
|
||||
/**
|
||||
* Records the declaration, so it could later participate in signature clash detection.
|
||||
*/
|
||||
fun trackDeclaration(declaration: Declaration, rawSignature: Signature) {
|
||||
declarationsBySignature.computeIfAbsent(rawSignature) { SmartSet.create() }.add(declaration)
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by [reportErrorsTo] whenever at least two declarations with the same [signature] are detected.
|
||||
*
|
||||
* Use [reportSignatureClashTo] in the implementation to report a diagnostic.
|
||||
*/
|
||||
protected abstract fun reportSignatureConflict(
|
||||
signature: Signature,
|
||||
declarations: Collection<Declaration>,
|
||||
diagnosticReporter: IrDiagnosticReporter,
|
||||
)
|
||||
|
||||
/**
|
||||
* Reports all detected signature clashes.
|
||||
*/
|
||||
open fun reportErrorsTo(diagnosticReporter: IrDiagnosticReporter) {
|
||||
for ((signature, declarations) in declarationsBySignature) {
|
||||
if (declarations.size <= 1) continue
|
||||
reportSignatureConflict(signature, declarations, diagnosticReporter)
|
||||
}
|
||||
}
|
||||
|
||||
protected inline fun <Data : Any, ConflictingDeclaration : IrDeclaration> reportSignatureClashTo(
|
||||
diagnosticReporter: IrDiagnosticReporter,
|
||||
diagnosticFactory: KtDiagnosticFactory1<Data>,
|
||||
declarations: Collection<ConflictingDeclaration>,
|
||||
data: Data,
|
||||
reportOnIfSynthetic: (ConflictingDeclaration) -> IrElement,
|
||||
) {
|
||||
declarations.mapTo(LinkedHashSet()) { declaration ->
|
||||
val reportOn = declaration.takeUnless { it.startOffset < 0 } ?: reportOnIfSynthetic(declaration)
|
||||
diagnosticReporter.at(reportOn.sourceElement(), reportOn, declaration.file)
|
||||
}.forEach {
|
||||
it.report(diagnosticFactory, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user