Prohibit operator mod as declaration and calls that resolved via it

#KT-24197 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2018-06-28 03:04:13 +03:00
parent c887b88ed9
commit 2e88f5c47d
16 changed files with 149 additions and 8 deletions
@@ -747,7 +747,9 @@ public interface Errors {
DiagnosticFactory2<KtBinaryExpression, KotlinType, KotlinType> FORBIDDEN_IDENTITY_EQUALS = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, FunctionDescriptor, String> DEPRECATED_BINARY_MOD = DiagnosticFactory2.create(WARNING);
DiagnosticFactory2<PsiElement, FunctionDescriptor, String> FORBIDDEN_BINARY_MOD = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, FunctionDescriptor, String> DEPRECATED_BINARY_MOD_AS_REM = DiagnosticFactory2.create(WARNING);
DiagnosticFactory2<PsiElement, FunctionDescriptor, String> FORBIDDEN_BINARY_MOD_AS_REM = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<KtArrayAccessExpression> NO_GET_METHOD = DiagnosticFactory0.create(ERROR, ARRAY_ACCESS);
DiagnosticFactory0<KtArrayAccessExpression> NO_SET_METHOD = DiagnosticFactory0.create(ERROR, ARRAY_ACCESS);
@@ -396,7 +396,9 @@ public class DefaultErrorMessages {
MAP.put(FORBIDDEN_IDENTITY_EQUALS, "Identity equality for arguments of types {0} and {1} is forbidden", RENDER_TYPE, RENDER_TYPE);
MAP.put(DEPRECATED_BINARY_MOD, "Deprecated convention for ''{0}''. Use ''{1}''", NAME, STRING);
MAP.put(FORBIDDEN_BINARY_MOD, "Convention for ''{0}'' is forbidden. Use ''{1}''", NAME, STRING);
MAP.put(DEPRECATED_BINARY_MOD_AS_REM, "''%'' is resolved to deprecated ''{0}'' operator. Replace with ''.{0}'' or add operator ''{1}''", NAME, STRING);
MAP.put(FORBIDDEN_BINARY_MOD_AS_REM, "''%'' is resolved to forbidden ''{0}'' operator. Replace with ''.{0}'' or add operator ''{1}''", NAME, STRING);
MAP.put(NO_GET_METHOD, "No get method providing array access");
MAP.put(NO_SET_METHOD, "No set method providing array access");
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -50,10 +51,18 @@ object OperatorModifierChecker {
checkSupportsFeature(LanguageFeature.OperatorProvideDelegate, languageVersionSettings, diagnosticHolder, modifier)
}
if (functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.values
&& languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)) {
if (functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.values &&
languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)
) {
val diagnosticFactory = if (!KotlinBuiltIns.isUnderKotlinPackage(descriptor) &&
languageVersionSettings.supportsFeature(LanguageFeature.ProhibitOperatorMod)
)
Errors.FORBIDDEN_BINARY_MOD
else
Errors.DEPRECATED_BINARY_MOD
val newNameConvention = REM_TO_MOD_OPERATION_NAMES.inverse()[functionDescriptor.name]
diagnosticHolder.report(Errors.DEPRECATED_BINARY_MOD.on(modifier, functionDescriptor, newNameConvention!!.asString()))
diagnosticHolder.report(diagnosticFactory.on(modifier, functionDescriptor, newNameConvention!!.asString()))
}
return
@@ -117,16 +117,26 @@ private fun checkModConvention(
if (KotlinBuiltIns.isUnderKotlinPackage(descriptor)) {
if (shouldWarnAboutDeprecatedModFromBuiltIns(languageVersionSettings)) {
addWarningAboutDeprecatedMod(descriptor, diagnosticHolder, modifier)
warnAboutDeprecatedOrForbiddenMod(descriptor, diagnosticHolder, modifier, languageVersionSettings)
}
} else {
if (languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)) {
addWarningAboutDeprecatedMod(descriptor, diagnosticHolder, modifier)
warnAboutDeprecatedOrForbiddenMod(descriptor, diagnosticHolder, modifier, languageVersionSettings)
}
}
}
private fun addWarningAboutDeprecatedMod(descriptor: FunctionDescriptor, diagnosticHolder: DiagnosticSink, reportOn: PsiElement) {
private fun warnAboutDeprecatedOrForbiddenMod(
descriptor: FunctionDescriptor,
diagnosticHolder: DiagnosticSink,
reportOn: PsiElement,
languageVersionSettings: LanguageVersionSettings
) {
val diagnosticFactory = if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitOperatorMod))
Errors.FORBIDDEN_BINARY_MOD_AS_REM
else
Errors.DEPRECATED_BINARY_MOD_AS_REM
val newNameConvention = OperatorConventions.REM_TO_MOD_OPERATION_NAMES.inverse()[descriptor.name]
diagnosticHolder.report(Errors.DEPRECATED_BINARY_MOD_AS_REM.on(reportOn, descriptor, newNameConvention!!.asString()))
diagnosticHolder.report(diagnosticFactory.on(reportOn, descriptor, newNameConvention!!.asString()))
}
@@ -0,0 +1,32 @@
// !LANGUAGE: +ProhibitOperatorMod
// !DIAGNOSTICS: -UNUSED_PARAMETER
object ModAndRem {
<!FORBIDDEN_BINARY_MOD!>operator<!> fun mod(x: Int) {}
operator fun rem(x: Int) {}
<!FORBIDDEN_BINARY_MOD!>operator<!> fun modAssign(x: Int) {}
operator fun remAssign(x: Int) {}
}
object JustMod {
<!FORBIDDEN_BINARY_MOD!>operator<!> fun mod(x: Int) {}
<!FORBIDDEN_BINARY_MOD!>operator<!> fun modAssign(x: Int) {}
}
fun foo() {
ModAndRem % 1
ModAndRem.mod(1)
ModAndRem.rem(1)
JustMod <!FORBIDDEN_BINARY_MOD_AS_REM!>%<!> 1
JustMod.mod(1)
val r = ModAndRem
r %= 1
r.remAssign(1)
val m = JustMod
m <!FORBIDDEN_BINARY_MOD_AS_REM!>%=<!> 1
m.modAssign(1)
}
@@ -0,0 +1,23 @@
package
public fun foo(): kotlin.Unit
public object JustMod {
private constructor JustMod()
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
}
public object ModAndRem {
private constructor ModAndRem()
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 final operator fun rem(/*0*/ x: kotlin.Int): kotlin.Unit
public final operator fun remAssign(/*0*/ x: kotlin.Int): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -13813,6 +13813,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/operatorRem/doNotResolveToInapplicableRem.kt");
}
@TestMetadata("forbiddenModOperatorConvention.kt")
public void testForbiddenModOperatorConvention() throws Exception {
runTest("compiler/testData/diagnostics/tests/operatorRem/forbiddenModOperatorConvention.kt");
}
@TestMetadata("modWithRemAssign.kt")
public void testModWithRemAssign() throws Exception {
runTest("compiler/testData/diagnostics/tests/operatorRem/modWithRemAssign.kt");
@@ -13813,6 +13813,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/operatorRem/doNotResolveToInapplicableRem.kt");
}
@TestMetadata("forbiddenModOperatorConvention.kt")
public void testForbiddenModOperatorConvention() throws Exception {
runTest("compiler/testData/diagnostics/tests/operatorRem/forbiddenModOperatorConvention.kt");
}
@TestMetadata("modWithRemAssign.kt")
public void testModWithRemAssign() throws Exception {
runTest("compiler/testData/diagnostics/tests/operatorRem/modWithRemAssign.kt");
@@ -77,6 +77,7 @@ enum class LanguageFeature(
AllowContractsForCustomFunctions(KOTLIN_1_3, kind = UNSTABLE_FEATURE),
ProhibitLocalAnnotations(KOTLIN_1_3, kind = BUG_FIX),
ProhibitSmartcastsOnLocalDelegatedProperty(KOTLIN_1_3, kind = BUG_FIX),
ProhibitOperatorMod(KOTLIN_1_3, kind = BUG_FIX),
StrictJavaNullabilityAssertions(sinceVersion = null, defaultState = State.DISABLED),
ProperIeee754Comparisons(sinceVersion = null, defaultState = State.DISABLED),
@@ -148,6 +148,8 @@ class QuickFixRegistrar : QuickFixContributor {
TYPE_CANT_BE_USED_FOR_CONST_VAL.registerFactory(removeModifierFactory)
DEPRECATED_BINARY_MOD.registerFactory(removeModifierFactory)
DEPRECATED_BINARY_MOD.registerFactory(RenameModToRemFix.Factory)
FORBIDDEN_BINARY_MOD.registerFactory(removeModifierFactory)
FORBIDDEN_BINARY_MOD.registerFactory(RenameModToRemFix.Factory)
UNRESOLVED_REFERENCE.registerFactory(ImportFix)
@@ -41,7 +41,7 @@ class RenameModToRemFix(element: KtNamedFunction, val newName: Name) : KotlinQui
companion object Factory : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
if (diagnostic.factory != Errors.DEPRECATED_BINARY_MOD) return null
if (diagnostic.factory != Errors.DEPRECATED_BINARY_MOD && diagnostic.factory != Errors.FORBIDDEN_BINARY_MOD) return null
val operatorMod = diagnostic.psiElement.getNonStrictParentOfType<KtNamedFunction>() ?: return null
val newName = REM_TO_MOD_OPERATION_NAMES.inverse()[operatorMod.nameAsName] ?: return null
@@ -0,0 +1,6 @@
// "Remove 'operator' modifier" "true"
// COMPILER_ARGUMENTS: -XXLanguage:+ProhibitOperatorMod
object A {
operator<caret> fun mod(x: Int) {}
}
@@ -0,0 +1,6 @@
// "Remove 'operator' modifier" "true"
// COMPILER_ARGUMENTS: -XXLanguage:+ProhibitOperatorMod
object A {
fun mod(x: Int) {}
}
@@ -0,0 +1,14 @@
// "Rename to 'rem'" "true"
// DISABLE-ERRORS
// COMPILER_ARGUMENTS: -XXLanguage:+ProhibitOperatorMod
object Rem {
operator<caret> fun mod(x: Int) {}
operator fun modAssign(x: Int) {}
}
fun test() {
Rem % 1
Rem.mod(1)
Rem.modAssign(1)
}
@@ -0,0 +1,14 @@
// "Rename to 'rem'" "true"
// DISABLE-ERRORS
// COMPILER_ARGUMENTS: -XXLanguage:+ProhibitOperatorMod
object Rem {
operator fun rem(x: Int) {}
operator fun modAssign(x: Int) {}
}
fun test() {
Rem % 1
Rem.rem(1)
Rem.modAssign(1)
}
@@ -7699,6 +7699,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
runTest("idea/testData/quickfix/modifiers/removeInnerForClassInTrait.kt");
}
@TestMetadata("removeModifierFromForbiddenOperatorMod.kt")
public void testRemoveModifierFromForbiddenOperatorMod() throws Exception {
runTest("idea/testData/quickfix/modifiers/removeModifierFromForbiddenOperatorMod.kt");
}
@TestMetadata("removeModifierFromOperatorMod.kt")
public void testRemoveModifierFromOperatorMod() throws Exception {
runTest("idea/testData/quickfix/modifiers/removeModifierFromOperatorMod.kt");
@@ -9287,6 +9292,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/renameToRem"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("forbiddenModAsMember.kt")
public void testForbiddenModAsMember() throws Exception {
runTest("idea/testData/quickfix/renameToRem/forbiddenModAsMember.kt");
}
@TestMetadata("modAsExtension.kt")
public void testModAsExtension() throws Exception {
runTest("idea/testData/quickfix/renameToRem/modAsExtension.kt");