Show warning for mod from built-ins since API=1.1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+44
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,10 +17,14 @@
|
||||
package org.jetbrains.kotlin.resolve.calls.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.name.isSubpackageOf
|
||||
import org.jetbrains.kotlin.psi.Call
|
||||
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
|
||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
|
||||
@@ -30,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isConventionCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
@@ -62,11 +67,7 @@ class OperatorCallChecker : CallChecker {
|
||||
val isConventionOperator = element is KtOperationReferenceExpression && element.isConventionOperator()
|
||||
|
||||
if (isConventionOperator) {
|
||||
val shouldUseOperatorRem = context.languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)
|
||||
if (functionDescriptor.name in OperatorConventions.REM_TO_MOD_OPERATION_NAMES.values && shouldUseOperatorRem) {
|
||||
val newNameConvention = OperatorConventions.REM_TO_MOD_OPERATION_NAMES.inverse()[functionDescriptor.name]
|
||||
context.trace.report(Errors.DEPRECATED_BINARY_MOD_AS_REM.on(reportOn, functionDescriptor, newNameConvention!!.asString()))
|
||||
}
|
||||
checkModConvention(functionDescriptor, context.languageVersionSettings, context.trace, reportOn)
|
||||
}
|
||||
|
||||
if (isConventionOperator || element is KtArrayAccessExpression) {
|
||||
@@ -99,3 +100,40 @@ class OperatorCallChecker : CallChecker {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun FunctionDescriptor.isOperatorMod(): Boolean {
|
||||
return this.isOperator && name in OperatorConventions.REM_TO_MOD_OPERATION_NAMES.values
|
||||
}
|
||||
|
||||
// This is an alternate function to KotlinBuiltIns.isBuiltIn
|
||||
// It is safer as it produces stable results independent of target platform (Java/JS/Native)
|
||||
fun FunctionDescriptor.hasSubpackageOfKotlin(): Boolean {
|
||||
val descriptorFqName = fqNameOrNull() ?: return false
|
||||
return descriptorFqName.isSubpackageOf(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME)
|
||||
}
|
||||
|
||||
fun shouldWarnAboutDeprecatedModFromBuiltIns(languageVersionSettings: LanguageVersionSettings): Boolean {
|
||||
return languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem) && languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_1
|
||||
}
|
||||
|
||||
private fun checkModConvention(descriptor: FunctionDescriptor, languageVersionSettings: LanguageVersionSettings,
|
||||
diagnosticHolder: DiagnosticSink, modifier: PsiElement) {
|
||||
if (!descriptor.isOperatorMod()) return
|
||||
|
||||
if (descriptor.hasSubpackageOfKotlin()) {
|
||||
if (shouldWarnAboutDeprecatedModFromBuiltIns(languageVersionSettings)) {
|
||||
addWarningAboutDeprecatedMod(descriptor, diagnosticHolder, modifier)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)) {
|
||||
addWarningAboutDeprecatedMod(descriptor, diagnosticHolder, modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addWarningAboutDeprecatedMod(descriptor: FunctionDescriptor, diagnosticHolder: DiagnosticSink, reportOn: PsiElement) {
|
||||
val newNameConvention = OperatorConventions.REM_TO_MOD_OPERATION_NAMES.inverse()[descriptor.name]
|
||||
diagnosticHolder.report(Errors.DEPRECATED_BINARY_MOD_AS_REM.on(reportOn, descriptor, newNameConvention!!.asString()))
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.resolve
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
@@ -29,15 +28,15 @@ import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.isSubpackageOf
|
||||
import org.jetbrains.kotlin.resolve.DeprecationLevelValue.*
|
||||
import org.jetbrains.kotlin.resolve.annotations.argumentValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.hasSubpackageOfKotlin
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.isOperatorMod
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.shouldWarnAboutDeprecatedModFromBuiltIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.SinceKotlinInfo
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
|
||||
private val JAVA_DEPRECATED = FqName("java.lang.Deprecated")
|
||||
@@ -186,8 +185,10 @@ private fun DeclarationDescriptor.getOwnDeprecations(languageVersionSettings: La
|
||||
}
|
||||
|
||||
// The problem is that declaration `mod` in built-ins has @Deprecated annotation but actually it was deprecated only in version 1.1
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem) && isOperatorModFromBuiltIns(this)) {
|
||||
return emptyList()
|
||||
if (this is FunctionDescriptor && this.isOperatorMod() && this.hasSubpackageOfKotlin()) {
|
||||
if (!shouldWarnAboutDeprecatedModFromBuiltIns(languageVersionSettings)) {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
val result = SmartList<Deprecation>()
|
||||
@@ -281,14 +282,3 @@ fun DeclarationDescriptor.isHiddenInResolution(languageVersionSettings: Language
|
||||
|
||||
return isDeprecatedHidden(languageVersionSettings)
|
||||
}
|
||||
|
||||
private fun isOperatorModFromBuiltIns(descriptor: DeclarationDescriptor): Boolean {
|
||||
if (descriptor is FunctionDescriptor && descriptor.isOperator) {
|
||||
val isOperatorMod = descriptor.name == OperatorNameConventions.MOD
|
||||
|
||||
val descriptorFqName = descriptor.fqNameOrNull() ?: return false
|
||||
return isOperatorMod && descriptorFqName.isSubpackageOf(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// !API_VERSION: 1.0
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER, -UNUSED_VARIABLE, -EXTENSION_SHADOWED_BY_MEMBER
|
||||
|
||||
class Foo {
|
||||
operator fun rem(x: Int): Foo = Foo()
|
||||
}
|
||||
|
||||
class Bar {
|
||||
operator fun remAssign(x: Int) {}
|
||||
}
|
||||
|
||||
class Baz {
|
||||
companion object {
|
||||
operator fun rem(x: Int) {}
|
||||
operator fun Int.rem(x: Int) {}
|
||||
}
|
||||
}
|
||||
|
||||
operator fun Baz.rem(x: Int) {}
|
||||
|
||||
fun local() {
|
||||
operator fun Int.rem(x: Int) {}
|
||||
operator fun String.remAssign(x: Int) {}
|
||||
}
|
||||
|
||||
class WithMod {
|
||||
<!DEPRECATED_BINARY_MOD!>operator<!> fun mod(other: WithMod) = this
|
||||
|
||||
fun test() {
|
||||
val a = this <!DEPRECATED_BINARY_MOD_AS_REM!>%<!> this
|
||||
var b = this.mod(this)
|
||||
b <!DEPRECATED_BINARY_MOD_AS_REM!>%=<!> this
|
||||
}
|
||||
}
|
||||
|
||||
fun noOverflow() {
|
||||
(-1).mod(5)
|
||||
}
|
||||
|
||||
fun builtIns(b: Byte, s: Short) {
|
||||
var a = 1 % 2
|
||||
a %= 3
|
||||
1.mod(2)
|
||||
b % s
|
||||
1.0 % 2.0
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package
|
||||
|
||||
public fun builtIns(/*0*/ b: kotlin.Byte, /*1*/ s: kotlin.Short): kotlin.Unit
|
||||
public fun local(): kotlin.Unit
|
||||
public fun noOverflow(): kotlin.Unit
|
||||
public operator fun Baz.rem(/*0*/ x: kotlin.Int): kotlin.Unit
|
||||
|
||||
public final class Bar {
|
||||
public constructor Bar()
|
||||
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 remAssign(/*0*/ x: kotlin.Int): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class Baz {
|
||||
public constructor Baz()
|
||||
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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public companion object Companion {
|
||||
private constructor Companion()
|
||||
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 rem(/*0*/ x: kotlin.Int): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
public final operator fun kotlin.Int.rem(/*0*/ x: kotlin.Int): kotlin.Unit
|
||||
}
|
||||
}
|
||||
|
||||
public final class Foo {
|
||||
public constructor Foo()
|
||||
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 rem(/*0*/ x: kotlin.Int): Foo
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class WithMod {
|
||||
public constructor WithMod()
|
||||
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*/ other: WithMod): WithMod
|
||||
public final fun test(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -13795,6 +13795,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noWarningForModFromBuiltinsWhenApi1_0.kt")
|
||||
public void testNoWarningForModFromBuiltinsWhenApi1_0() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/operatorRem/noWarningForModFromBuiltinsWhenApi1_0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("numberRemConversions.kt")
|
||||
public void testNumberRemConversions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/operatorRem/numberRemConversions.kt");
|
||||
|
||||
Reference in New Issue
Block a user