Report error on accidentally "overriding" wait/notify
Hard-code only Object members because diagnostics are reported for all other classes out of the box in 1.1 #KT-7174 Fixed
This commit is contained in:
+81
-30
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.load.java.descriptors.getParentJavaStaticClassScope
|
|||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.*
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
@@ -40,7 +39,18 @@ import java.util.*
|
|||||||
private val EXTERNAL_SOURCES_KINDS = arrayOf(
|
private val EXTERNAL_SOURCES_KINDS = arrayOf(
|
||||||
JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS,
|
JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS,
|
||||||
JvmDeclarationOriginKind.DELEGATION,
|
JvmDeclarationOriginKind.DELEGATION,
|
||||||
JvmDeclarationOriginKind.BRIDGE)
|
JvmDeclarationOriginKind.BRIDGE
|
||||||
|
)
|
||||||
|
|
||||||
|
private val PREDEFINED_SIGNATURES = listOf(
|
||||||
|
"notify()V",
|
||||||
|
"notifyAll()V",
|
||||||
|
"wait()V",
|
||||||
|
"wait(J)V",
|
||||||
|
"wait(JI)V"
|
||||||
|
).map { signature ->
|
||||||
|
RawSignature(signature.substringBefore('('), signature.substring(signature.indexOf('(')), MemberKind.METHOD)
|
||||||
|
}
|
||||||
|
|
||||||
class BuilderFactoryForDuplicateSignatureDiagnostics(
|
class BuilderFactoryForDuplicateSignatureDiagnostics(
|
||||||
builderFactory: ClassBuilderFactory,
|
builderFactory: ClassBuilderFactory,
|
||||||
@@ -94,7 +104,25 @@ class BuilderFactoryForDuplicateSignatureDiagnostics(
|
|||||||
classInternalName: String,
|
classInternalName: String,
|
||||||
signatures: MultiMap<RawSignature, JvmDeclarationOrigin>
|
signatures: MultiMap<RawSignature, JvmDeclarationOrigin>
|
||||||
) {
|
) {
|
||||||
reportDiagnosticsTasks.add { reportClashingSignaturesInHierarchy(classOrigin, classInternalName, signatures) }
|
reportDiagnosticsTasks.add {
|
||||||
|
reportClashingWithPredefinedSignatures(classOrigin, classInternalName, signatures)
|
||||||
|
reportClashingSignaturesInHierarchy(classOrigin, classInternalName, signatures)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reportClashingWithPredefinedSignatures(
|
||||||
|
classOrigin: JvmDeclarationOrigin,
|
||||||
|
classInternalName: String,
|
||||||
|
signatures: MultiMap<RawSignature, JvmDeclarationOrigin>
|
||||||
|
) {
|
||||||
|
for (predefinedSignature in PREDEFINED_SIGNATURES) {
|
||||||
|
if (!signatures.containsKey(predefinedSignature)) continue
|
||||||
|
|
||||||
|
val origins = signatures[predefinedSignature] + JvmDeclarationOrigin.NO_ORIGIN
|
||||||
|
|
||||||
|
val diagnostic = computeDiagnosticToReport(classOrigin, classInternalName, predefinedSignature, origins) ?: continue
|
||||||
|
diagnostics.report(ErrorsJvm.CONFLICTING_INHERITED_JVM_DECLARATIONS.on(diagnostic.element, diagnostic.data))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportClashingSignaturesInHierarchy(
|
private fun reportClashingSignaturesInHierarchy(
|
||||||
@@ -114,41 +142,64 @@ class BuilderFactoryForDuplicateSignatureDiagnostics(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signatures@
|
|
||||||
for ((rawSignature, origins) in groupedBySignature.entrySet()) {
|
for ((rawSignature, origins) in groupedBySignature.entrySet()) {
|
||||||
if (origins.size <= 1) continue
|
if (origins.size <= 1) continue
|
||||||
|
|
||||||
var memberElement: PsiElement? = null
|
val diagnostic = computeDiagnosticToReport(classOrigin, classInternalName, rawSignature, origins)
|
||||||
var ownNonFakeCount = 0
|
|
||||||
for (origin in origins) {
|
|
||||||
val member = origin.descriptor as? CallableMemberDescriptor?
|
|
||||||
if (member != null && member.containingDeclaration == classOrigin.descriptor && member.kind != FAKE_OVERRIDE) {
|
|
||||||
ownNonFakeCount++
|
|
||||||
// If there's more than one real element, the clashing signature is already reported.
|
|
||||||
// Only clashes between fake overrides are interesting here
|
|
||||||
if (ownNonFakeCount > 1) continue@signatures
|
|
||||||
|
|
||||||
if (member.kind != DELEGATION) {
|
when (diagnostic) {
|
||||||
// Delegates don't have declarations in the code
|
is ConflictingDeclarationError.AccidentalOverride -> {
|
||||||
memberElement = origin.element ?: DescriptorToSourceUtils.descriptorToDeclaration(member)
|
diagnostics.report(ErrorsJvm.ACCIDENTAL_OVERRIDE.on(diagnostic.element, diagnostic.data))
|
||||||
if (memberElement == null && member is PropertyAccessorDescriptor) {
|
}
|
||||||
memberElement = DescriptorToSourceUtils.descriptorToDeclaration(member.correspondingProperty)
|
is ConflictingDeclarationError.ConflictingInheritedJvmDeclarations -> {
|
||||||
}
|
diagnostics.report(ErrorsJvm.CONFLICTING_INHERITED_JVM_DECLARATIONS.on(diagnostic.element, diagnostic.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ConflictingDeclarationError(val element: PsiElement, val data: ConflictingJvmDeclarationsData) {
|
||||||
|
class AccidentalOverride(element: PsiElement, data: ConflictingJvmDeclarationsData) :
|
||||||
|
ConflictingDeclarationError(element, data)
|
||||||
|
class ConflictingInheritedJvmDeclarations(element: PsiElement, data: ConflictingJvmDeclarationsData) :
|
||||||
|
ConflictingDeclarationError(element, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeDiagnosticToReport(
|
||||||
|
classOrigin: JvmDeclarationOrigin,
|
||||||
|
classInternalName: String,
|
||||||
|
rawSignature: RawSignature,
|
||||||
|
origins: Collection<JvmDeclarationOrigin>
|
||||||
|
): ConflictingDeclarationError? {
|
||||||
|
var memberElement: PsiElement? = null
|
||||||
|
var ownNonFakeCount = 0
|
||||||
|
for (origin in origins) {
|
||||||
|
val member = origin.descriptor as? CallableMemberDescriptor?
|
||||||
|
if (member != null && member.containingDeclaration == classOrigin.descriptor && member.kind != FAKE_OVERRIDE) {
|
||||||
|
ownNonFakeCount++
|
||||||
|
// If there's more than one real element, the clashing signature is already reported.
|
||||||
|
// Only clashes between fake overrides are interesting here
|
||||||
|
if (ownNonFakeCount > 1) return null
|
||||||
|
|
||||||
|
if (member.kind != DELEGATION) {
|
||||||
|
// Delegates don't have declarations in the code
|
||||||
|
memberElement = origin.element ?: DescriptorToSourceUtils.descriptorToDeclaration(member)
|
||||||
|
if (memberElement == null && member is PropertyAccessorDescriptor) {
|
||||||
|
memberElement = DescriptorToSourceUtils.descriptorToDeclaration(member.correspondingProperty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val elementToReportOn = memberElement ?: classOrigin.element
|
|
||||||
if (elementToReportOn == null) return // TODO: it'd be better to report this error without any element at all
|
|
||||||
|
|
||||||
val data = ConflictingJvmDeclarationsData(classInternalName, classOrigin, rawSignature, origins)
|
|
||||||
if (memberElement != null) {
|
|
||||||
diagnostics.report(ErrorsJvm.ACCIDENTAL_OVERRIDE.on(elementToReportOn, data))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
diagnostics.report(ErrorsJvm.CONFLICTING_INHERITED_JVM_DECLARATIONS.on(elementToReportOn, data))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val data = ConflictingJvmDeclarationsData(classInternalName, classOrigin, rawSignature, origins)
|
||||||
|
if (memberElement != null) {
|
||||||
|
return ConflictingDeclarationError.AccidentalOverride(memberElement, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: it'd be better to report this error without any element at all
|
||||||
|
val elementToReportOn = classOrigin.element ?: return null
|
||||||
|
|
||||||
|
return ConflictingDeclarationError.ConflictingInheritedJvmDeclarations(elementToReportOn, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun groupMembersDescriptorsBySignature(descriptor: ClassDescriptor): MultiMap<RawSignature, JvmDeclarationOrigin> {
|
private fun groupMembersDescriptorsBySignature(descriptor: ClassDescriptor): MultiMap<RawSignature, JvmDeclarationOrigin> {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
interface MyTrait: <!PLATFORM_CLASS_MAPPED_TO_KOTLIN, INTERFACE_WITH_SUPERCLASS!>Object<!> {
|
interface MyTrait: <!PLATFORM_CLASS_MAPPED_TO_KOTLIN, INTERFACE_WITH_SUPERCLASS!>Object<!> {
|
||||||
override fun toString(): String
|
override fun toString(): String
|
||||||
public override fun finalize()
|
public override fun finalize()
|
||||||
public <!OVERRIDING_FINAL_MEMBER!>override<!> fun wait()
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>public <!OVERRIDING_FINAL_MEMBER!>override<!> fun wait()<!>
|
||||||
}
|
}
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
|
// KT-7174 Report error on members with the same signature as non-overridable methods from mapped Java types (like Object.wait/notify)
|
||||||
|
|
||||||
|
class A {
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun notify()<!> {}
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun notifyAll()<!> {}
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun wait()<!> {}
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun wait(l: Long)<!> {}
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun wait(l: Long, i: Int)<!> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun notify()<!> {}
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun notifyAll()<!> {}
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun wait()<!> {}
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun wait(l: Long)<!> {}
|
||||||
|
<!CONFLICTING_INHERITED_JVM_DECLARATIONS!>fun wait(l: Long, i: Int)<!> {}
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun notify(): kotlin.Unit
|
||||||
|
public fun notifyAll(): kotlin.Unit
|
||||||
|
public fun wait(): kotlin.Unit
|
||||||
|
public fun wait(/*0*/ l: kotlin.Long): kotlin.Unit
|
||||||
|
public fun wait(/*0*/ l: kotlin.Long, /*1*/ i: kotlin.Int): kotlin.Unit
|
||||||
|
|
||||||
|
public final class A {
|
||||||
|
public constructor A()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public final fun notify(): kotlin.Unit
|
||||||
|
public final fun notifyAll(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
public final fun wait(): kotlin.Unit
|
||||||
|
public final fun wait(/*0*/ l: kotlin.Long): kotlin.Unit
|
||||||
|
public final fun wait(/*0*/ l: kotlin.Long, /*1*/ i: kotlin.Int): kotlin.Unit
|
||||||
|
}
|
||||||
@@ -5865,6 +5865,21 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/diagnostics/tests/duplicateJvmSignature/finalMembersFromBuiltIns")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class FinalMembersFromBuiltIns extends AbstractDiagnosticsTest {
|
||||||
|
public void testAllFilesPresentInFinalMembersFromBuiltIns() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/duplicateJvmSignature/finalMembersFromBuiltIns"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("waitNotify.kt")
|
||||||
|
public void testWaitNotify() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/duplicateJvmSignature/finalMembersFromBuiltIns/waitNotify.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/duplicateJvmSignature/functionAndProperty")
|
@TestMetadata("compiler/testData/diagnostics/tests/duplicateJvmSignature/functionAndProperty")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user