Prohibit identity equals on inline class types
This commit is contained in:
@@ -744,6 +744,7 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory2<KtBinaryExpression, KotlinType, KotlinType> DEPRECATED_IDENTITY_EQUALS = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<KtBinaryExpression, KotlinType, KotlinType> IMPLICIT_BOXING_IN_IDENTITY_EQUALS = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<KtBinaryExpression, KotlinType, KotlinType> FORBIDDEN_IDENTITY_EQUALS = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory2<PsiElement, FunctionDescriptor, String> DEPRECATED_BINARY_MOD = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<PsiElement, FunctionDescriptor, String> DEPRECATED_BINARY_MOD_AS_REM = DiagnosticFactory2.create(WARNING);
|
||||
|
||||
+1
@@ -393,6 +393,7 @@ public class DefaultErrorMessages {
|
||||
|
||||
MAP.put(DEPRECATED_IDENTITY_EQUALS, "Identity equality for arguments of types {0} and {1} is deprecated", RENDER_TYPE, RENDER_TYPE);
|
||||
MAP.put(IMPLICIT_BOXING_IN_IDENTITY_EQUALS, "Identity equality for arguments of types {0} and {1} can be unstable because of implicit boxing", RENDER_TYPE, RENDER_TYPE);
|
||||
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(DEPRECATED_BINARY_MOD_AS_REM, "''%'' is resolved to deprecated ''{0}'' operator. Replace with ''.{0}'' or add operator ''{1}''", NAME, STRING);
|
||||
|
||||
+12
-5
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.lexer.KtKeywordToken;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.ReservedCheckingKt;
|
||||
import org.jetbrains.kotlin.resolve.*;
|
||||
@@ -1069,7 +1068,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
ensureNonemptyIntersectionOfOperandTypes(expression, context);
|
||||
// TODO : Check comparison pointlessness
|
||||
result = TypeInfoFactoryKt.createTypeInfo(components.builtIns.getBooleanType(), context);
|
||||
checkIdentityOnPrimitiveTypes(expression, context);
|
||||
checkIdentityOnPrimitiveOrInlineClassTypes(expression, context);
|
||||
}
|
||||
else if (OperatorConventions.IN_OPERATIONS.contains(operationType)) {
|
||||
ValueArgument leftArgument = CallMaker.makeValueArgument(left, left != null ? left : operationSign);
|
||||
@@ -1091,15 +1090,23 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
return components.dataFlowAnalyzer.checkType(result, expression, contextWithExpectedType);
|
||||
}
|
||||
|
||||
private static void checkIdentityOnPrimitiveTypes(@NotNull KtBinaryExpression expression, @NotNull ExpressionTypingContext context) {
|
||||
private static void checkIdentityOnPrimitiveOrInlineClassTypes(
|
||||
@NotNull KtBinaryExpression expression,
|
||||
@NotNull ExpressionTypingContext context
|
||||
) {
|
||||
if (expression.getLeft() == null || expression.getRight() == null) return;
|
||||
|
||||
KotlinType leftType = context.trace.getType(expression.getLeft());
|
||||
KotlinType rightType = context.trace.getType(expression.getRight());
|
||||
if (leftType == null || rightType == null) return;
|
||||
|
||||
if (KotlinTypeChecker.DEFAULT.equalTypes(leftType, rightType) && KotlinBuiltIns.isPrimitiveType(leftType)) {
|
||||
context.trace.report(DEPRECATED_IDENTITY_EQUALS.on(expression, leftType, rightType));
|
||||
if (KotlinTypeChecker.DEFAULT.equalTypes(leftType, rightType)) {
|
||||
if (KotlinBuiltIns.isPrimitiveType(leftType)) {
|
||||
context.trace.report(DEPRECATED_IDENTITY_EQUALS.on(expression, leftType, rightType));
|
||||
}
|
||||
else if (InlineClassesUtilsKt.isInlineClassType(leftType)) {
|
||||
context.trace.report(FORBIDDEN_IDENTITY_EQUALS.on(expression, leftType, rightType));
|
||||
}
|
||||
}
|
||||
else if (isIdentityComparedWithImplicitBoxing(leftType, rightType) || isIdentityComparedWithImplicitBoxing(rightType, leftType)) {
|
||||
context.trace.report(IMPLICIT_BOXING_IN_IDENTITY_EQUALS.on(expression, leftType, rightType));
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
inline class Foo(val x: Int)
|
||||
inline class Bar(val y: String)
|
||||
|
||||
fun test(f1: Foo, f2: Foo, b1: Bar, fn1: Foo?, fn2: Foo?) {
|
||||
val a1 = <!FORBIDDEN_IDENTITY_EQUALS!>f1 === f2<!> || <!FORBIDDEN_IDENTITY_EQUALS!>f1 !== f2<!>
|
||||
val a2 = <!FORBIDDEN_IDENTITY_EQUALS!>f1 === f1<!>
|
||||
val a3 = <!EQUALITY_NOT_APPLICABLE!>f1 === b1<!> || <!EQUALITY_NOT_APPLICABLE!>f1 !== b1<!>
|
||||
|
||||
val c1 = <!FORBIDDEN_IDENTITY_EQUALS!>fn1 === fn2<!> || <!FORBIDDEN_IDENTITY_EQUALS!>fn1 !== fn2<!>
|
||||
val c2 = f1 === fn1 || f1 !== fn1
|
||||
val c3 = <!EQUALITY_NOT_APPLICABLE!>b1 === fn1<!> || <!EQUALITY_NOT_APPLICABLE!>b1 !== fn1<!>
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package
|
||||
|
||||
public fun test(/*0*/ f1: Foo, /*1*/ f2: Foo, /*2*/ b1: Bar, /*3*/ fn1: Foo?, /*4*/ fn2: Foo?): kotlin.Unit
|
||||
|
||||
public final inline class Bar {
|
||||
public constructor Bar(/*0*/ y: kotlin.String)
|
||||
public final val y: kotlin.String
|
||||
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 final inline class Foo {
|
||||
public constructor Foo(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
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
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// !SKIP_METADATA_VERSION_CHECK
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
fun test(
|
||||
ub1: UByte, ub2: UByte,
|
||||
us1: UShort, us2: UShort,
|
||||
ui1: UInt, ui2: UInt,
|
||||
ul1: ULong, ul2: ULong
|
||||
) {
|
||||
val ub = <!FORBIDDEN_IDENTITY_EQUALS!>ub1 === ub2<!> || <!FORBIDDEN_IDENTITY_EQUALS!>ub1 !== ub2<!>
|
||||
val us = <!FORBIDDEN_IDENTITY_EQUALS!>us1 === us2<!> || <!FORBIDDEN_IDENTITY_EQUALS!>us1 !== us2<!>
|
||||
val ui = <!FORBIDDEN_IDENTITY_EQUALS!>ui1 === ui2<!> || <!FORBIDDEN_IDENTITY_EQUALS!>ui1 !== ui2<!>
|
||||
val ul = <!FORBIDDEN_IDENTITY_EQUALS!>ul1 === ul2<!> || <!FORBIDDEN_IDENTITY_EQUALS!>ul1 !== ul2<!>
|
||||
|
||||
val u = <!EQUALITY_NOT_APPLICABLE!>ub1 === ul1<!>
|
||||
|
||||
val a1 = <!FORBIDDEN_IDENTITY_EQUALS!>1u === 2u<!> || <!FORBIDDEN_IDENTITY_EQUALS!>1u !== 2u<!>
|
||||
val a2 = <!FORBIDDEN_IDENTITY_EQUALS!>0xFFFF_FFFF_FFFF_FFFFu === 0xFFFF_FFFF_FFFF_FFFFu<!>
|
||||
|
||||
val bu1 = 1u
|
||||
val bu2 = 1u
|
||||
|
||||
val c1 = <!FORBIDDEN_IDENTITY_EQUALS!>bu1 === bu2<!> || <!FORBIDDEN_IDENTITY_EQUALS!>bu1 !== bu2<!>
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun test(/*0*/ ub1: kotlin.UByte, /*1*/ ub2: kotlin.UByte, /*2*/ us1: kotlin.UShort, /*3*/ us2: kotlin.UShort, /*4*/ ui1: kotlin.UInt, /*5*/ ui2: kotlin.UInt, /*6*/ ul1: kotlin.ULong, /*7*/ ul2: kotlin.ULong): kotlin.Unit
|
||||
@@ -10706,6 +10706,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/basicInlineClassDeclarationDisabled.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("identityComparisonWithInlineClasses.kt")
|
||||
public void testIdentityComparisonWithInlineClasses() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/identityComparisonWithInlineClasses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineClassDeclarationCheck.kt")
|
||||
public void testInlineClassDeclarationCheck() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt");
|
||||
|
||||
+5
@@ -29,6 +29,11 @@ public class DiagnosticsWithUnsignedTypesGenerated extends AbstractDiagnosticsWi
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithUnsignedTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("forbiddenEqualsOnUnsignedTypes.kt")
|
||||
public void testForbiddenEqualsOnUnsignedTypes() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithUnsignedTypes/forbiddenEqualsOnUnsignedTypes.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overloadResolutionOfBasicOperations.kt")
|
||||
public void testOverloadResolutionOfBasicOperations() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithUnsignedTypes/overloadResolutionOfBasicOperations.kt");
|
||||
|
||||
Generated
+5
@@ -10706,6 +10706,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/basicInlineClassDeclarationDisabled.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("identityComparisonWithInlineClasses.kt")
|
||||
public void testIdentityComparisonWithInlineClasses() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/identityComparisonWithInlineClasses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineClassDeclarationCheck.kt")
|
||||
public void testInlineClassDeclarationCheck() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt");
|
||||
|
||||
Reference in New Issue
Block a user