[FIR] KT-55747: Report error for `operator fun mod`

^KT-55747 Fixed

Merge-request: KT-MR-8522
Merged-by: Nikolay Lunyak <Nikolay.Lunyak@jetbrains.com>
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt
index e04d8e8..4badab0 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt
@@ -387,6 +387,22 @@
             token,
         )
     }
+    add(FirErrors.FORBIDDEN_BINARY_MOD) { firDiagnostic ->
+        ForbiddenBinaryModImpl(
+            firSymbolBuilder.buildSymbol(firDiagnostic.a),
+            firDiagnostic.b,
+            firDiagnostic as KtPsiDiagnostic,
+            token,
+        )
+    }
+    add(FirErrors.DEPRECATED_BINARY_MOD) { firDiagnostic ->
+        DeprecatedBinaryModImpl(
+            firSymbolBuilder.buildSymbol(firDiagnostic.a),
+            firDiagnostic.b,
+            firDiagnostic as KtPsiDiagnostic,
+            token,
+        )
+    }
     add(FirErrors.SUPER_IS_NOT_AN_EXPRESSION) { firDiagnostic ->
         SuperIsNotAnExpressionImpl(
             firDiagnostic as KtPsiDiagnostic,
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt
index b446c2b..5226d8c 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt
@@ -307,6 +307,18 @@
         abstract val altererNames: List<String?>
     }
 
+    abstract class ForbiddenBinaryMod : KtFirDiagnostic<PsiElement>() {
+        override val diagnosticClass get() = ForbiddenBinaryMod::class
+        abstract val forbiddenFunction: KtSymbol
+        abstract val suggestedFunction: String
+    }
+
+    abstract class DeprecatedBinaryMod : KtFirDiagnostic<PsiElement>() {
+        override val diagnosticClass get() = DeprecatedBinaryMod::class
+        abstract val forbiddenFunction: KtSymbol
+        abstract val suggestedFunction: String
+    }
+
     abstract class SuperIsNotAnExpression : KtFirDiagnostic<PsiElement>() {
         override val diagnosticClass get() = SuperIsNotAnExpression::class
     }
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt
index ef344e1..c42a25e 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt
@@ -355,6 +355,20 @@
     override val token: KtLifetimeToken,
 ) : KtFirDiagnostic.AmbiguousAlteredAssign(), KtAbstractFirDiagnostic<PsiElement>
 
+internal class ForbiddenBinaryModImpl(
+    override val forbiddenFunction: KtSymbol,
+    override val suggestedFunction: String,
+    override val firDiagnostic: KtPsiDiagnostic,
+    override val token: KtLifetimeToken,
+) : KtFirDiagnostic.ForbiddenBinaryMod(), KtAbstractFirDiagnostic<PsiElement>
+
+internal class DeprecatedBinaryModImpl(
+    override val forbiddenFunction: KtSymbol,
+    override val suggestedFunction: String,
+    override val firDiagnostic: KtPsiDiagnostic,
+    override val token: KtLifetimeToken,
+) : KtFirDiagnostic.DeprecatedBinaryMod(), KtAbstractFirDiagnostic<PsiElement>
+
 internal class SuperIsNotAnExpressionImpl(
     override val firDiagnostic: KtPsiDiagnostic,
     override val token: KtLifetimeToken,
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 b09d8b2..5dd3234 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
@@ -35404,6 +35404,12 @@
         }
 
         @Test
+        @TestMetadata("kt55747.kt")
+        public void testKt55747() throws Exception {
+            runTest("compiler/testData/diagnostics/testsWithStdLib/kt55747.kt");
+        }
+
+        @Test
         @TestMetadata("kt8050.kt")
         public void testKt8050() throws Exception {
             runTest("compiler/testData/diagnostics/testsWithStdLib/kt8050.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 66af377..86dd508 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
@@ -35500,6 +35500,12 @@
         }
 
         @Test
+        @TestMetadata("kt55747.kt")
+        public void testKt55747() throws Exception {
+            runTest("compiler/testData/diagnostics/testsWithStdLib/kt55747.kt");
+        }
+
+        @Test
         @TestMetadata("kt8050.kt")
         public void testKt8050() throws Exception {
             runTest("compiler/testData/diagnostics/testsWithStdLib/kt8050.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 e7336fe..7fb07f7 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
@@ -35404,6 +35404,12 @@
         }
 
         @Test
+        @TestMetadata("kt55747.kt")
+        public void testKt55747() throws Exception {
+            runTest("compiler/testData/diagnostics/testsWithStdLib/kt55747.kt");
+        }
+
+        @Test
         @TestMetadata("kt8050.kt")
         public void testKt8050() throws Exception {
             runTest("compiler/testData/diagnostics/testsWithStdLib/kt8050.kt");
diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt
index a7d58b1..9692dfd 100644
--- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt
+++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt
@@ -152,6 +152,14 @@
         val AMBIGUOUS_ALTERED_ASSIGN by error<PsiElement> {
             parameter<List<String?>>("altererNames")
         }
+        val FORBIDDEN_BINARY_MOD by error<PsiElement>(PositioningStrategy.OPERATOR_MODIFIER) {
+            parameter<FirBasedSymbol<*>>("forbiddenFunction")
+            parameter<String>("suggestedFunction")
+        }
+        val DEPRECATED_BINARY_MOD by error<PsiElement>(PositioningStrategy.OPERATOR_MODIFIER) {
+            parameter<FirBasedSymbol<*>>("forbiddenFunction")
+            parameter<String>("suggestedFunction")
+        }
     }
 
     val SUPER by object : DiagnosticGroup("Super") {
diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt
index 84c2821..73ac111 100644
--- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt
+++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt
@@ -164,6 +164,8 @@
     val FUNCTION_EXPECTED by error2<PsiElement, String, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
     val RESOLUTION_TO_CLASSIFIER by error1<PsiElement, FirRegularClassSymbol>()
     val AMBIGUOUS_ALTERED_ASSIGN by error1<PsiElement, List<String?>>()
+    val FORBIDDEN_BINARY_MOD by error2<PsiElement, FirBasedSymbol<*>, String>(SourceElementPositioningStrategies.OPERATOR_MODIFIER)
+    val DEPRECATED_BINARY_MOD by error2<PsiElement, FirBasedSymbol<*>, String>(SourceElementPositioningStrategies.OPERATOR_MODIFIER)
 
     // Super
     val SUPER_IS_NOT_AN_EXPRESSION by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
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 9bbbdff..574c091 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
@@ -19,7 +19,6 @@
 import org.jetbrains.kotlin.fir.analysis.checkers.isSupertypeOf
 import org.jetbrains.kotlin.fir.analysis.checkers.overriddenFunctions
 import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
-import org.jetbrains.kotlin.diagnostics.reportOn
 import org.jetbrains.kotlin.fir.containingClassLookupTag
 import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
 import org.jetbrains.kotlin.fir.declarations.utils.isInline
@@ -32,6 +31,8 @@
 import org.jetbrains.kotlin.lexer.KtTokens
 import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.name.StandardClassIds
+import org.jetbrains.kotlin.name.isSubpackageOf
+import org.jetbrains.kotlin.util.OperatorNameConventions
 import org.jetbrains.kotlin.util.OperatorNameConventions.ASSIGNMENT_OPERATIONS
 import org.jetbrains.kotlin.util.OperatorNameConventions.BINARY_OPERATION_NAMES
 import org.jetbrains.kotlin.util.OperatorNameConventions.COMPARE_TO
@@ -76,8 +77,24 @@
                 return
             }
         }
+
+        checkReplaceableLegacyOperators(declaration, context, reporter)
     }
 
+    private fun checkReplaceableLegacyOperators(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
+        val replacement = OperatorNameConventions.MOD_OPERATORS_REPLACEMENT[declaration.name] ?: return
+
+        val diagnostic = if (
+            declaration.symbol.callableId.packageName.isSubpackageOf(StandardClassIds.BASE_KOTLIN_PACKAGE) ||
+            !context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitOperatorMod)
+        ) {
+            FirErrors.DEPRECATED_BINARY_MOD
+        } else {
+            FirErrors.FORBIDDEN_BINARY_MOD
+        }
+
+        reporter.reportOn(declaration.source, diagnostic, declaration.symbol, replacement.asString(), context)
+    }
 }
 
 private interface Check : (CheckerContext, FirSimpleFunction) -> String? {
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt
index ede68b4..f44b09e 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt
@@ -214,6 +214,8 @@
 import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FINAL_SUPERTYPE
 import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FINAL_UPPER_BOUND
 import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FLOAT_LITERAL_OUT_OF_RANGE
+import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_BINARY_MOD
+import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_BINARY_MOD
 import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE
 import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_CALL_EXPECTED
 import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_DECLARATION_WITH_NO_NAME
@@ -712,6 +714,8 @@
             "Multiple extensions tried to alter this assignement at the same time. Extensions: {0}",
             COLLECTION(NULLABLE_STRING)
         )
+        map.put(DEPRECATED_BINARY_MOD, "Convention for ''{0}'' is forbidden. Use ''{1}''", SYMBOL, STRING)
+        map.put(FORBIDDEN_BINARY_MOD, "Deprecated convention for ''{0}''. Use ''{1}''", SYMBOL, STRING)
 
         map.put(ILLEGAL_SELECTOR, "The expression cannot be a selector (occur after a dot)")
         map.put(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property")
diff --git a/compiler/testData/codegen/box/operatorConventions/remAssignmentOperation.kt b/compiler/testData/codegen/box/operatorConventions/remAssignmentOperation.kt
index 79896e8..0e34c3c 100644
--- a/compiler/testData/codegen/box/operatorConventions/remAssignmentOperation.kt
+++ b/compiler/testData/codegen/box/operatorConventions/remAssignmentOperation.kt
@@ -4,6 +4,7 @@
     var x = 5
 }
 
+@Suppress("DEPRECATED_BINARY_MOD")
 operator fun A.modAssign(y: Int) { throw RuntimeException("mod has been called instead of rem") }
 operator fun A.remAssign(y: Int) { x %= y + 1 }
 
diff --git a/compiler/testData/codegen/box/operatorConventions/remOverModOperation.kt b/compiler/testData/codegen/box/operatorConventions/remOverModOperation.kt
index e5371fc..4ef742e 100644
--- a/compiler/testData/codegen/box/operatorConventions/remOverModOperation.kt
+++ b/compiler/testData/codegen/box/operatorConventions/remOverModOperation.kt
@@ -3,6 +3,7 @@
 class A() {
     var x = 5
 
+    @Suppress("DEPRECATED_BINARY_MOD")
     operator fun mod(y: Int) { throw RuntimeException("mod has been called instead of rem") }
     operator fun rem(y: Int) { x -= y }
 }
diff --git a/compiler/testData/diagnostics/tests/operatorRem/DeprecatedModAssignOperatorConventions.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/DeprecatedModAssignOperatorConventions.fir.kt
index 68a4881..d4c57fb3 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/DeprecatedModAssignOperatorConventions.fir.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/DeprecatedModAssignOperatorConventions.fir.kt
@@ -2,12 +2,12 @@
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
 class OldAndNew {
-    operator fun modAssign(x: Int) {}
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun modAssign(x: Int) {}
     operator fun remAssign(x: Int) {}
 }
 
 class OnlyOld {
-    operator fun modAssign(x: Int) {}
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun modAssign(x: Int) {}
 }
 
 class OnlyNew {
@@ -16,11 +16,11 @@
 
 class Sample
 
-operator fun Sample.modAssign(x: Int) {}
+<!DEPRECATED_BINARY_MOD!>operator<!> fun Sample.modAssign(x: Int) {}
 operator fun Sample.remAssign(x: Int) {}
 
 class ModAndRemAssign {
-    operator fun mod(x: Int): ModAndRemAssign = ModAndRemAssign()
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun mod(x: Int): ModAndRemAssign = ModAndRemAssign()
     operator fun remAssign(x: Int) {}
 }
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/DeprecatedModOperatorConventions.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/DeprecatedModOperatorConventions.fir.kt
index ebe52c3..50d476f 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/DeprecatedModOperatorConventions.fir.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/DeprecatedModOperatorConventions.fir.kt
@@ -2,12 +2,12 @@
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
 class OldAndNew {
-    operator fun mod(x: Int) {}
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun mod(x: Int) {}
     operator fun rem(x: Int) {}
 }
 
 class OnlyOld {
-    operator fun mod(x: Int) {}
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun mod(x: Int) {}
 }
 
 class OnlyNew {
@@ -16,11 +16,11 @@
 
 class Sample
 
-operator fun Sample.mod(x: Int) {}
+<!DEPRECATED_BINARY_MOD!>operator<!> fun Sample.mod(x: Int) {}
 operator fun Sample.rem(x: Int) {}
 
 class IntAndUnit {
-    operator fun mod(x: Int) = 0
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun mod(x: Int) = 0
     operator fun rem(x: Int): Int = 0
 }
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/deprecatedModConvention.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/deprecatedModConvention.fir.kt
index 7873fff..61d4eb6 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/deprecatedModConvention.fir.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/deprecatedModConvention.fir.kt
@@ -2,23 +2,23 @@
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
 object ModAndRem {
-    operator fun mod(x: Int) {}
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun mod(x: Int) {}
     operator fun rem(x: Int) {}
 }
 
 object OldMod {
-    operator fun mod(x: Int) {}
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun mod(x: Int) {}
 }
 
 object ModAndRemExtension
-operator fun ModAndRemExtension.mod(x: Int) {}
+<!DEPRECATED_BINARY_MOD!>operator<!> fun ModAndRemExtension.mod(x: Int) {}
 operator fun ModAndRemExtension.rem(x: Int) {}
 
 object ModExtension
-operator fun ModExtension.mod(x: Int) {}
+<!DEPRECATED_BINARY_MOD!>operator<!> fun ModExtension.mod(x: Int) {}
 
 object ModMemberAndRemExtension {
-    operator fun mod(x: Int) {}
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun mod(x: Int) {}
 }
 
 operator fun ModMemberAndRemExtension.rem(x: Int) {}
diff --git a/compiler/testData/diagnostics/tests/operatorRem/doNotResolveToInapplicableRem.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/doNotResolveToInapplicableRem.fir.kt
index e6965a5..331b844 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/doNotResolveToInapplicableRem.fir.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/doNotResolveToInapplicableRem.fir.kt
@@ -2,7 +2,7 @@
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
 object OldMod {
-    operator fun mod(x: Int) {}
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun mod(x: Int) {}
 }
 
 object RemExtension
diff --git a/compiler/testData/diagnostics/tests/operatorRem/forbiddenModOperatorConvention.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/forbiddenModOperatorConvention.fir.kt
index 842be5c..f145162 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/forbiddenModOperatorConvention.fir.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/forbiddenModOperatorConvention.fir.kt
@@ -2,16 +2,16 @@
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
 object ModAndRem {
-    operator fun mod(x: Int) {}
+    <!FORBIDDEN_BINARY_MOD!>operator<!> fun mod(x: Int) {}
     operator fun rem(x: Int) {}
 
-    operator fun modAssign(x: Int) {}
+    <!FORBIDDEN_BINARY_MOD!>operator<!> fun modAssign(x: Int) {}
     operator fun remAssign(x: Int) {}
 }
 
 object JustMod {
-    operator fun mod(x: Int) {}
-    operator fun modAssign(x: Int) {}
+    <!FORBIDDEN_BINARY_MOD!>operator<!> fun mod(x: Int) {}
+    <!FORBIDDEN_BINARY_MOD!>operator<!> fun modAssign(x: Int) {}
 }
 
 fun foo() {
diff --git a/compiler/testData/diagnostics/tests/operatorRem/modWithRemAssign.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/modWithRemAssign.fir.kt
deleted file mode 100644
index 1f88d50..0000000
--- a/compiler/testData/diagnostics/tests/operatorRem/modWithRemAssign.fir.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-// !LANGUAGE: -ProhibitOperatorMod
-// !DIAGNOSTICS: -UNUSED_PARAMETER, -EXTENSION_SHADOWED_BY_MEMBER
-
-class ModAndRemAssign {
-    operator fun mod(x: Int) = ModAndRemAssign()
-    operator fun mod(x: String) = ModAndRemAssign()
-    operator fun modAssign(x: String) {}
-    operator fun remAssign(x: Int) {}
-}
-
-operator fun ModAndRemAssign.mod(x: String) = ModAndRemAssign()
-operator fun ModAndRemAssign.modAssign(x: String) {}
-
-fun test() {
-    val modAndRemAssign = ModAndRemAssign()
-    modAndRemAssign %= 1
-}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/operatorRem/modWithRemAssign.kt b/compiler/testData/diagnostics/tests/operatorRem/modWithRemAssign.kt
index 2f6fdef..efe449c 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/modWithRemAssign.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/modWithRemAssign.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
 // !LANGUAGE: -ProhibitOperatorMod
 // !DIAGNOSTICS: -UNUSED_PARAMETER, -EXTENSION_SHADOWED_BY_MEMBER
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemAsExtentionOverMod.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemAsExtentionOverMod.fir.kt
deleted file mode 100644
index 3695372..0000000
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemAsExtentionOverMod.fir.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-// !LANGUAGE: -ProhibitOperatorMod
-// !DIAGNOSTICS: -UNUSED_PARAMETER
-
-class Foo {
-}
-operator fun Foo.mod(x: Int): Foo = Foo()
-operator fun Foo.rem(x: Int): Int = 0
-
-fun foo() {
-    takeInt(Foo() % 1)
-}
-
-fun takeInt(x: Int) {}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemAsExtentionOverMod.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemAsExtentionOverMod.kt
index 56c6a55..58acaad 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemAsExtentionOverMod.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/preferRemAsExtentionOverMod.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
 // !LANGUAGE: -ProhibitOperatorMod
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemAsMemberOverMod.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemAsMemberOverMod.fir.kt
deleted file mode 100644
index eec75b3..0000000
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemAsMemberOverMod.fir.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-// !LANGUAGE: -ProhibitOperatorMod
-// !DIAGNOSTICS: -UNUSED_PARAMETER
-
-class Foo {
-    operator fun mod(x: Int): Foo = Foo()
-    operator fun rem(x: Int): Int = 0
-}
-
-fun foo() {
-    takeInt(Foo() % 1)
-}
-
-fun takeInt(x: Int) {}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemAsMemberOverMod.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemAsMemberOverMod.kt
index 1adf05b..5cf456a 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemAsMemberOverMod.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/preferRemAsMemberOverMod.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
 // !LANGUAGE: -ProhibitOperatorMod
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemFromCompanionObjectOverRem.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemFromCompanionObjectOverRem.fir.kt
deleted file mode 100644
index acf62c6..0000000
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemFromCompanionObjectOverRem.fir.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-// !LANGUAGE: -ProhibitOperatorMod
-// !DIAGNOSTICS: -UNUSED_PARAMETER
-
-class A {
-    companion object {
-        operator fun A.rem(x: Int) = 0
-    }
-
-    fun test() {
-        operator fun A.mod(x: Int) = ""
-
-        takeInt(A() % 123)
-    }
-}
-
-fun takeInt(x: Int) {}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemFromCompanionObjectOverRem.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemFromCompanionObjectOverRem.kt
index 17a7880..0a5fa68 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemFromCompanionObjectOverRem.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/preferRemFromCompanionObjectOverRem.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
 // !LANGUAGE: -ProhibitOperatorMod
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemOverModInLocalFunctions.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemOverModInLocalFunctions.fir.kt
deleted file mode 100644
index d705914..0000000
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemOverModInLocalFunctions.fir.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-// !LANGUAGE: -ProhibitOperatorMod
-// !DIAGNOSTICS: -UNUSED_PARAMETER
-
-object B
-
-class A {
-    operator fun B.rem(x: Int) = 0
-}
-
-fun test1() {
-    operator fun B.mod(x: Int) = ""
-
-    with(A()) {
-        takeInt(B % 10)
-    }
-}
-
-class C {
-    operator fun B.mod(x: Int) = ""
-}
-
-fun test2() {
-    operator fun B.rem(x: Int) = 0
-
-    with(C()) {
-        takeInt(B % 10)
-    }
-}
-
-fun takeInt(x: Int) {}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemOverModInLocalFunctions.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemOverModInLocalFunctions.kt
index 0e7f7d3..89a6d92 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemOverModInLocalFunctions.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/preferRemOverModInLocalFunctions.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
 // !LANGUAGE: -ProhibitOperatorMod
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemWithImplicitReceivers.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemWithImplicitReceivers.fir.kt
deleted file mode 100644
index e1a2d48..0000000
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemWithImplicitReceivers.fir.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-// !LANGUAGE: -ProhibitOperatorMod
-// !DIAGNOSTICS: -UNUSED_PARAMETER
-
-class A {
-    operator fun Int.mod(s: String) = 4
-}
-
-class B {
-    operator fun Int.rem(s: String) = ""
-}
-
-fun test() {
-    with(B()) {
-        with(A()) {
-            takeString(1 % "")
-        }
-    }
-}
-
-fun takeString(s: String) {}
diff --git a/compiler/testData/diagnostics/tests/operatorRem/preferRemWithImplicitReceivers.kt b/compiler/testData/diagnostics/tests/operatorRem/preferRemWithImplicitReceivers.kt
index 67caa7f..d6783ee 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/preferRemWithImplicitReceivers.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/preferRemWithImplicitReceivers.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
 // !LANGUAGE: -ProhibitOperatorMod
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/prefereRemAsExtensionOverMemberMod.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/prefereRemAsExtensionOverMemberMod.fir.kt
deleted file mode 100644
index 0b7bcae..0000000
--- a/compiler/testData/diagnostics/tests/operatorRem/prefereRemAsExtensionOverMemberMod.fir.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-// !LANGUAGE: -ProhibitOperatorMod
-// !DIAGNOSTICS: -UNUSED_PARAMETER
-
-class Foo {
-    operator fun mod(x: Int): Foo = Foo()
-}
-
-operator fun Foo.rem(x: Int): Int = 0
-
-
-fun foo() {
-    takeInt(Foo() % 1)
-}
-
-fun takeInt(x: Int) {}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/operatorRem/prefereRemAsExtensionOverMemberMod.kt b/compiler/testData/diagnostics/tests/operatorRem/prefereRemAsExtensionOverMemberMod.kt
index 78fa07f..3d8e86a 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/prefereRemAsExtensionOverMemberMod.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/prefereRemAsExtensionOverMemberMod.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
 // !LANGUAGE: -ProhibitOperatorMod
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/remWithModAndModAssign.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/remWithModAndModAssign.fir.kt
deleted file mode 100644
index 530b505..0000000
--- a/compiler/testData/diagnostics/tests/operatorRem/remWithModAndModAssign.fir.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-// !LANGUAGE: -ProhibitOperatorMod
-// !DIAGNOSTICS: -UNUSED_PARAMETER, -EXTENSION_SHADOWED_BY_MEMBER
-
-class ModAndRemAssign {
-    operator fun mod(x: Int) = ModAndRemAssign()
-    operator fun mod(x: String) = ModAndRemAssign()
-    operator fun modAssign(x: String) {}
-    operator fun rem(x: Int) = ModAndRemAssign()
-}
-
-operator fun ModAndRemAssign.mod(x: String) = ModAndRemAssign()
-operator fun ModAndRemAssign.modAssign(x: String) {}
-
-fun test() {
-    var modAndRemAssign = ModAndRemAssign()
-    modAndRemAssign %= 1
-}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/operatorRem/remWithModAndModAssign.kt b/compiler/testData/diagnostics/tests/operatorRem/remWithModAndModAssign.kt
index 38b40ff..848d425 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/remWithModAndModAssign.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/remWithModAndModAssign.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
 // !LANGUAGE: -ProhibitOperatorMod
 // !DIAGNOSTICS: -UNUSED_PARAMETER, -EXTENSION_SHADOWED_BY_MEMBER
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/remWithModAssign.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/remWithModAssign.fir.kt
deleted file mode 100644
index 2ceabb2..0000000
--- a/compiler/testData/diagnostics/tests/operatorRem/remWithModAssign.fir.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-// !LANGUAGE: -ProhibitOperatorMod
-// !DIAGNOSTICS: -UNUSED_PARAMETER
-
-object RemAndModAssign {
-    operator fun modAssign(x: String) {}
-    operator fun rem(x: Int) = RemAndModAssign
-}
-
-fun test2() {
-    var c = RemAndModAssign
-    c %= 123
-}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/operatorRem/remWithModAssign.kt b/compiler/testData/diagnostics/tests/operatorRem/remWithModAssign.kt
index 3ff810b..d9532df 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/remWithModAssign.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/remWithModAssign.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
 // !LANGUAGE: -ProhibitOperatorMod
 // !DIAGNOSTICS: -UNUSED_PARAMETER
 
diff --git a/compiler/testData/diagnostics/tests/operatorRem/resolveModIfRemIsHidden.fir.kt b/compiler/testData/diagnostics/tests/operatorRem/resolveModIfRemIsHidden.fir.kt
index 008c29c..29f164f 100644
--- a/compiler/testData/diagnostics/tests/operatorRem/resolveModIfRemIsHidden.fir.kt
+++ b/compiler/testData/diagnostics/tests/operatorRem/resolveModIfRemIsHidden.fir.kt
@@ -6,7 +6,7 @@
     @Deprecated("Use mod instead", ReplaceWith("mod"), DeprecationLevel.HIDDEN)
     operator fun rem(x: Int) = 0
 
-    operator fun mod(x: Int) = ""
+    <!DEPRECATED_BINARY_MOD!>operator<!> fun mod(x: Int) = ""
 }
 
 fun test() {
diff --git a/compiler/testData/diagnostics/testsWithStdLib/kt55747.kt b/compiler/testData/diagnostics/testsWithStdLib/kt55747.kt
new file mode 100644
index 0000000..0b9c5b8
--- /dev/null
+++ b/compiler/testData/diagnostics/testsWithStdLib/kt55747.kt
@@ -0,0 +1,6 @@
+// FIR_IDENTICAL
+
+object Rem {
+    <!FORBIDDEN_BINARY_MOD!>operator<!> fun mod(x: Int) {}
+    <!FORBIDDEN_BINARY_MOD!>operator<!> fun modAssign(x: Int) {}
+}
diff --git a/compiler/testData/diagnostics/testsWithStdLib/kt55747.txt b/compiler/testData/diagnostics/testsWithStdLib/kt55747.txt
new file mode 100644
index 0000000..b50472c
--- /dev/null
+++ b/compiler/testData/diagnostics/testsWithStdLib/kt55747.txt
@@ -0,0 +1,10 @@
+package
+
+public object Rem {
+    private constructor Rem()
+    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 operator fun mod(/*0*/ x: kotlin.Int): kotlin.Unit
+    public final operator fun modAssign(/*0*/ x: kotlin.Int): kotlin.Unit
+    public open override /*1*/ /*fake_override*/ 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 95264f5..91ffbe9 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
@@ -35500,6 +35500,12 @@
         }
 
         @Test
+        @TestMetadata("kt55747.kt")
+        public void testKt55747() throws Exception {
+            runTest("compiler/testData/diagnostics/testsWithStdLib/kt55747.kt");
+        }
+
+        @Test
         @TestMetadata("kt8050.kt")
         public void testKt8050() throws Exception {
             runTest("compiler/testData/diagnostics/testsWithStdLib/kt8050.kt");
diff --git a/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt b/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt
index 73a41f8..5e14b0e 100644
--- a/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt
+++ b/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt
@@ -92,4 +92,7 @@
 
     @JvmField
     val DELEGATED_PROPERTY_OPERATORS = setOf(GET_VALUE, SET_VALUE, PROVIDE_DELEGATE)
+
+    @JvmField
+    val MOD_OPERATORS_REPLACEMENT = mapOf(MOD to REM, MOD_ASSIGN to REM_ASSIGN)
 }