mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Kotlin] Consider supertypes in 'array in data class' quickfix
KTIJ-28464 GitOrigin-RevId: 35c20e8401be48af0cc166b38f7603b4fbf62f73
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cf4c3f5d8b
commit
d710a02263
+32
-11
@@ -231,25 +231,32 @@ object GenerateEqualsAndHashCodeUtils {
|
||||
|
||||
context(KaSession)
|
||||
private fun findEqualsMethodForClass(classSymbol: KaClassSymbol): KaCallableSymbol? =
|
||||
findMethodInMemberScopeOrInAny(classSymbol, EQUALS) { matchesEqualsMethodSignature(it) }
|
||||
findNonGeneratedMethodInSelfOrSuperclass(classSymbol, EQUALS) { matchesEqualsMethodSignature(it) }
|
||||
|
||||
context(KaSession)
|
||||
private fun findHashCodeMethodForClass(classSymbol: KaClassSymbol): KaCallableSymbol? =
|
||||
findMethodInMemberScopeOrInAny(classSymbol, HASH_CODE) { matchesHashCodeMethodSignature(it) }
|
||||
findNonGeneratedMethodInSelfOrSuperclass(classSymbol, HASH_CODE) { matchesHashCodeMethodSignature(it) }
|
||||
|
||||
/**
|
||||
* Searches for a callable member symbol with the given [methodName] that matches the [signatureFilter].
|
||||
* If the found symbol is generated, the search is done for one more time in the superclass' scope
|
||||
*/
|
||||
context(KaSession)
|
||||
private fun findMethodInMemberScopeOrInAny(
|
||||
private fun findNonGeneratedMethodInSelfOrSuperclass(
|
||||
classSymbol: KaClassSymbol,
|
||||
methodName: Name,
|
||||
signatureFilter: (KaNamedFunctionSymbol) -> Boolean
|
||||
): KaCallableSymbol? {
|
||||
findMethod(classSymbol, methodName) { callableSymbol ->
|
||||
if (callableSymbol !is KaNamedFunctionSymbol) return@findMethod false
|
||||
signatureFilter(callableSymbol) && callableSymbol.origin != KaSymbolOrigin.SOURCE_MEMBER_GENERATED
|
||||
}?.let { return it }
|
||||
val methodSymbol = findMethod(classSymbol, methodName, signatureFilter)
|
||||
|
||||
val anySuperClassSymbol = classSymbol.superTypes.find { it.isAnyType }?.symbol as? KaClassSymbol ?: return null
|
||||
return anySuperClassSymbol.memberScope.callables(methodName).singleOrNull()
|
||||
// We are not interested in synthetic members of data classes here
|
||||
// They won't be generated by the compiler after the explicit members are created (see the Kotlin Specification, 4.1.2)
|
||||
if (methodSymbol?.origin != KaSymbolOrigin.SOURCE_MEMBER_GENERATED) return methodSymbol
|
||||
|
||||
// Instead, if a generated member was found, we search for a member again in its parent class' scope to find a relevant member
|
||||
val directSuperclassSymbol = findExplicitSuperclassOrAny(classSymbol) ?: return null
|
||||
|
||||
return findMethod(directSuperclassSymbol, methodName, signatureFilter)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,8 +265,22 @@ object GenerateEqualsAndHashCodeUtils {
|
||||
*/
|
||||
context(KaSession)
|
||||
private fun findMethod(
|
||||
classSymbol: KaClassSymbol, methodName: Name, condition: (KaCallableSymbol) -> Boolean
|
||||
): KaCallableSymbol? = classSymbol.memberScope.callables(methodName).filter(condition).singleOrNull()
|
||||
classSymbol: KaClassSymbol, methodName: Name, condition: (KaNamedFunctionSymbol) -> Boolean
|
||||
): KaCallableSymbol? = classSymbol.memberScope.callables(methodName).filter {
|
||||
it is KaNamedFunctionSymbol && condition(it)
|
||||
}.singleOrNull()
|
||||
|
||||
/**
|
||||
* Searches for the direct superclass symbol of this [classSymbol] (ignoring interfaces).
|
||||
* Because currently `kotlin.Any` is not listed in symbol's supertypes when all declared supertypes are interfaces,
|
||||
* this case is handled separately.
|
||||
*/
|
||||
context(KaSession)
|
||||
private fun findExplicitSuperclassOrAny(classSymbol: KaClassSymbol): KaClassSymbol? {
|
||||
val supertypes = classSymbol.superTypes
|
||||
return supertypes.map { it.symbol }.filterIsInstance<KaClassSymbol>().singleOrNull { it.classKind == KaClassKind.CLASS }
|
||||
?: supertypes.first().allSupertypes.singleOrNull { it.isAnyType }?.symbol as? KaClassSymbol
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to generate the "not equals" comparison between the class of `this` and the class of the parameter.
|
||||
|
||||
+15
@@ -6661,11 +6661,26 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("blankSupertypes.kt")
|
||||
public void testBlankSupertypes() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayInDataClass/blankSupertypes.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("changedParameterNameInParent.kt")
|
||||
public void testChangedParameterNameInParent() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayInDataClass/changedParameterNameInParent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("genericArray.kt")
|
||||
public void testGenericArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayInDataClass/genericArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("indirectOverride.kt")
|
||||
public void testIndirectOverride() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayInDataClass/indirectOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("intArray.kt")
|
||||
public void testIntArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayInDataClass/intArray.kt");
|
||||
|
||||
+15
@@ -82,11 +82,26 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("blankSupertypes.kt")
|
||||
public void testBlankSupertypes() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayInDataClass/blankSupertypes.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("changedParameterNameInParent.kt")
|
||||
public void testChangedParameterNameInParent() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayInDataClass/changedParameterNameInParent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("genericArray.kt")
|
||||
public void testGenericArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayInDataClass/genericArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("indirectOverride.kt")
|
||||
public void testIndirectOverride() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayInDataClass/indirectOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("intArray.kt")
|
||||
public void testIntArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayInDataClass/intArray.kt");
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
interface I1
|
||||
interface I2
|
||||
interface I3 : I2
|
||||
|
||||
open class C
|
||||
open class D : C()
|
||||
|
||||
data class A(val <caret>a: IntArray): D(), I1, I3
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
interface I1
|
||||
interface I2
|
||||
interface I3 : I2
|
||||
|
||||
open class C
|
||||
open class D : C()
|
||||
|
||||
data class A(val a: IntArray): D(), I1, I3 {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as A
|
||||
|
||||
if (!a.contentEquals(other.a)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return a.contentHashCode()
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
data class A(val <caret>a: IntArray) : Parent("some", intArrayOf(1, 2, 3))
|
||||
|
||||
open class Parent(
|
||||
val some: String,
|
||||
val array: IntArray,
|
||||
) {
|
||||
override fun equals(renamed: Any?): Boolean {
|
||||
if (this === renamed) return true
|
||||
if (javaClass != renamed?.javaClass) return false
|
||||
|
||||
renamed as Parent
|
||||
|
||||
if (some != renamed.some) return false
|
||||
if (!array.contentEquals(renamed.array)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = some.hashCode()
|
||||
result = 31 * result + array.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
data class A(val a: IntArray) : Parent("some", intArrayOf(1, 2, 3)) {
|
||||
override fun equals(renamed: Any?): Boolean {
|
||||
if (this === renamed) return true
|
||||
if (javaClass != renamed?.javaClass) return false
|
||||
if (!super.equals(renamed)) return false
|
||||
|
||||
renamed as A
|
||||
|
||||
if (!a.contentEquals(renamed.a)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + a.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
open class Parent(
|
||||
val some: String,
|
||||
val array: IntArray,
|
||||
) {
|
||||
override fun equals(renamed: Any?): Boolean {
|
||||
if (this === renamed) return true
|
||||
if (javaClass != renamed?.javaClass) return false
|
||||
|
||||
renamed as Parent
|
||||
|
||||
if (some != renamed.some) return false
|
||||
if (!array.contentEquals(renamed.array)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = some.hashCode()
|
||||
result = 31 * result + array.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
open class GrandParent {
|
||||
override fun equals(renamed: Any?): Boolean {
|
||||
return super.equals(renamed)
|
||||
}
|
||||
}
|
||||
|
||||
open class Parent() : GrandParent()
|
||||
|
||||
data class A(val <caret>a: IntArray) : Parent()
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
open class GrandParent {
|
||||
override fun equals(renamed: Any?): Boolean {
|
||||
return super.equals(renamed)
|
||||
}
|
||||
}
|
||||
|
||||
open class Parent() : GrandParent()
|
||||
|
||||
data class A(val a: IntArray) : Parent() {
|
||||
override fun equals(renamed: Any?): Boolean {
|
||||
if (this === renamed) return true
|
||||
if (javaClass != renamed?.javaClass) return false
|
||||
if (!super.equals(renamed)) return false
|
||||
|
||||
renamed as A
|
||||
|
||||
if (!a.contentEquals(renamed.a)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return a.contentHashCode()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user