'operator' keyword for 'typed' equals in inline classes allowed
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java
index 5bdc9fa..a4765ba 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java
@@ -17849,6 +17849,12 @@
}
@Test
+ @TestMetadata("illegalEqualsOverridingInInlineClass.kt")
+ public void testIllegalEqualsOverridingInInlineClass() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt");
+ }
+
+ @Test
@TestMetadata("inlineClassCanImplementInterfaceByDelegation.kt")
public void testInlineClassCanImplementInterfaceByDelegation() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassCanImplementInterfaceByDelegation.kt");
@@ -17957,6 +17963,12 @@
}
@Test
+ @TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
+ public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
+ }
+
+ @Test
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/unsignedLiteralsWithoutArtifactOnClasspath.kt");
diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java
index 99f3c86..40b1ea0 100644
--- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java
+++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java
@@ -17849,6 +17849,12 @@
}
@Test
+ @TestMetadata("illegalEqualsOverridingInInlineClass.kt")
+ public void testIllegalEqualsOverridingInInlineClass() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt");
+ }
+
+ @Test
@TestMetadata("inlineClassCanImplementInterfaceByDelegation.kt")
public void testInlineClassCanImplementInterfaceByDelegation() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassCanImplementInterfaceByDelegation.kt");
@@ -17957,6 +17963,12 @@
}
@Test
+ @TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
+ public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
+ }
+
+ @Test
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/unsignedLiteralsWithoutArtifactOnClasspath.kt");
diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java
index 290b280..3321eac 100644
--- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java
+++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java
@@ -17849,6 +17849,12 @@
}
@Test
+ @TestMetadata("illegalEqualsOverridingInInlineClass.kt")
+ public void testIllegalEqualsOverridingInInlineClass() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt");
+ }
+
+ @Test
@TestMetadata("inlineClassCanImplementInterfaceByDelegation.kt")
public void testInlineClassCanImplementInterfaceByDelegation() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassCanImplementInterfaceByDelegation.kt");
@@ -17957,6 +17963,12 @@
}
@Test
+ @TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
+ public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
+ }
+
+ @Test
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/unsignedLiteralsWithoutArtifactOnClasspath.kt");
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOperatorModifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOperatorModifierChecker.kt
index 27e09e0..1fca77a 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOperatorModifierChecker.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOperatorModifierChecker.kt
@@ -25,6 +25,7 @@
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
+import org.jetbrains.kotlin.fir.declarations.utils.isInline
import org.jetbrains.kotlin.fir.declarations.utils.isOperator
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
@@ -184,10 +185,21 @@
checkFor(
EQUALS,
member,
- Checks.full("must override ''equals()'' in Any") { ctx, function ->
- val containingClassSymbol = function.containingClass()?.toFirRegularClassSymbol(ctx.session) ?: return@full true
- function.overriddenFunctions(containingClassSymbol, ctx).any {
- it.containingClass()?.classId == StandardClassIds.Any
+ object : Check {
+ override fun check(context: CheckerContext, function: FirSimpleFunction): String? {
+ val containingClassSymbol = function.containingClass()?.toFirRegularClassSymbol(context.session) ?: return null
+ if (function.overriddenFunctions(containingClassSymbol, context)
+ .any { it.containingClass()?.classId == StandardClassIds.Any }
+ || function.isTypedEqualsInInlineClass(context.session)
+ ) {
+ return null
+ }
+ return buildString {
+ append("must override ''equals()'' in Any")
+ if (containingClassSymbol.isInline) {
+ append(" or define ''equals(other: ${containingClassSymbol.name}): Boolean''")
+ }
+ }
}
}
)
diff --git a/compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt b/compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt
new file mode 100644
index 0000000..48c2ff1
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt
@@ -0,0 +1,27 @@
+// FIR_IDENTICAL
+// WITH_STDLIB
+// !DIAGNOSTICS: -DEBUG_INFO_SMARTCAST
+
+@JvmInline
+value class IC1(val x: Int) {
+ <!ILLEGAL_EQUALS_OVERRIDING_IN_INLINE_CLASS!>override fun equals(other: Any?): Boolean<!> {
+ if (other !is IC1) {
+ return false
+ }
+ return x == other.x
+ }
+}
+
+@JvmInline
+value class IC2(val x: Int) {
+ override fun hashCode() = 0
+}
+
+@JvmInline
+value class IC3(val x: Int) {
+ override fun equals(other: Any?) = true
+
+ fun equals(other: IC3) = true
+
+ override fun hashCode() = 0
+}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.txt b/compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.txt
new file mode 100644
index 0000000..06e3b9d
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.txt
@@ -0,0 +1,26 @@
+package
+
+@kotlin.jvm.JvmInline public final value class IC1 {
+ public constructor IC1(/*0*/ x: kotlin.Int)
+ public final val x: kotlin.Int
+ public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
+ public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
+}
+
+@kotlin.jvm.JvmInline public final value class IC2 {
+ public constructor IC2(/*0*/ x: kotlin.Int)
+ public final val x: kotlin.Int
+ public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ fun hashCode(): kotlin.Int
+ public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
+}
+
+@kotlin.jvm.JvmInline public final value class IC3 {
+ public constructor IC3(/*0*/ x: kotlin.Int)
+ public final val x: kotlin.Int
+ public final fun equals(/*0*/ other: IC3): kotlin.Boolean
+ public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ fun hashCode(): kotlin.Int
+ public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
+}
diff --git a/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt b/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt
new file mode 100644
index 0000000..26dc6c9
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt
@@ -0,0 +1,20 @@
+// FIR_IDENTICAL
+// WITH_STDLIB
+// !DIAGNOSTICS: -DEBUG_INFO_SMARTCAST
+
+@JvmInline
+value class IC1(val x: Int) {
+ override fun equals(other: Any?) = true
+
+ operator fun equals(other: IC1) = true
+
+ override fun hashCode() = 0
+}
+
+@JvmInline
+value class IC2(val x: Int) {
+ <!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun equals(other: IC1) = true
+
+ <!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun equals(other: IC2) {
+ }
+}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.txt b/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.txt
new file mode 100644
index 0000000..e304e65
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.txt
@@ -0,0 +1,20 @@
+package
+
+@kotlin.jvm.JvmInline public final value class IC1 {
+ public constructor IC1(/*0*/ x: kotlin.Int)
+ public final val x: kotlin.Int
+ public final operator fun equals(/*0*/ other: IC1): kotlin.Boolean
+ public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ fun hashCode(): kotlin.Int
+ public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
+}
+
+@kotlin.jvm.JvmInline public final value class IC2 {
+ public constructor IC2(/*0*/ x: kotlin.Int)
+ public final val x: kotlin.Int
+ public final operator fun equals(/*0*/ other: IC1): kotlin.Boolean
+ public final operator fun equals(/*0*/ other: IC2): kotlin.Unit
+ public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
+ public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
+}
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java
index 9dc8d50..a0ad526 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java
@@ -17855,6 +17855,12 @@
}
@Test
+ @TestMetadata("illegalEqualsOverridingInInlineClass.kt")
+ public void testIllegalEqualsOverridingInInlineClass() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/inlineClasses/illegalEqualsOverridingInInlineClass.kt");
+ }
+
+ @Test
@TestMetadata("inlineClassCanImplementInterfaceByDelegation.kt")
public void testInlineClassCanImplementInterfaceByDelegation() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassCanImplementInterfaceByDelegation.kt");
@@ -17963,6 +17969,12 @@
}
@Test
+ @TestMetadata("typedEqualsOperatorModifierInInlineClass.kt")
+ public void testTypedEqualsOperatorModifierInInlineClass() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt");
+ }
+
+ @Test
@TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt")
public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/unsignedLiteralsWithoutArtifactOnClasspath.kt");
diff --git a/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt b/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt
index 5fb6beb..0f61c7e 100644
--- a/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt
+++ b/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt
@@ -24,6 +24,7 @@
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.module
+import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
@@ -198,8 +199,14 @@
Checks(RANGE_UNTIL, MemberOrExtension, SingleValueParameter, NoDefaultAndVarargsCheck),
Checks(EQUALS, Member) {
fun DeclarationDescriptor.isAny() = this is ClassDescriptor && KotlinBuiltIns.isAny(this)
- ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() }) {
- "must override ''equals()'' in Any"
+ ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() }
+ || (containingDeclaration.isInlineClass() && isTypedEqualsInInlineClass())) {
+ buildString {
+ append("must override ''equals()'' in Any")
+ if (containingDeclaration.isInlineClass()) {
+ append(" or define ''equals(other: ${containingDeclaration.name}): Boolean''")
+ }
+ }
}
},
Checks(COMPARE_TO, MemberOrExtension, ReturnsInt, SingleValueParameter, NoDefaultAndVarargsCheck),