[FIR] Fix K2 behavior according to RULES1
The compiler should only report diagnostics for
comparisons over builtins and identity-less types,
other incompatibilities should be reported
via inspections.
It's ok that in `equalityChecksOnIntegerTypes`
instead of `EQUALITY_NOT_APPLICABLE_WARNING` we get
`EQUALITY_NOT_APPLICABLE`, because
`ProperEqualityChecksInBuilderInferenceCalls`
is already active by default.
This change also replaces the notion of a representative superclass
with the least upper bound.
This makes complex types like
intersection/flexible transparent to
RULES1-based compatibility checks.
One way to look at it is to think
that this is an automatic way of handling
type parameters: automatic picking of
"interesting" bounds, and checking them against one another.
Note that `TypeIntersector.intersectTypes`
for `Int` and `T` where `T` is a type parameter
may return both `{Int & T}` or `null`
depending on `T`-s bounds. At the same time,
for type parameters `T` and `K` it will
always return `{T & K}`.
`ConeTypeIntersector.intersectTypes`, on the
other hand, will always return `{Int & T}`
irrespectively of the bounds. Meaning, the two
intersectors differ in corner cases.
`lowerBoundIfFlexible` call in `isLiterallyTypeParameter` is backed by
the `equalityOfFlexibleTypeParameters` test.
^KT-35134 #fixed-in-k2
^KT-22499 #fixed-in-k2
^KT-46383 #fixed-in-k2
This commit is contained in:
committed by
Space Team
parent
06e687addd
commit
f0720c1d12
+1
-1
@@ -33,7 +33,7 @@ fun case1(javaClass: JavaClass?) {
|
||||
}
|
||||
|
||||
class Case1(val javaClass: JavaClass?) {
|
||||
val x = if (javaClass != null) { it -> <!EQUALITY_NOT_APPLICABLE_WARNING!>it == javaClass<!> } else BooCase2.FILTER
|
||||
val x = if (javaClass != null) { it -> it == javaClass } else BooCase2.FILTER
|
||||
}
|
||||
|
||||
class BooCase1() {
|
||||
|
||||
@@ -101,7 +101,7 @@ fun test_9(a: Int, b: Int?) {
|
||||
}
|
||||
b<!UNSAFE_CALL!>.<!>inc()
|
||||
|
||||
if (a === b) {
|
||||
if (<!IMPLICIT_BOXING_IN_IDENTITY_EQUALS!>a === b<!>) {
|
||||
b.inc()
|
||||
}
|
||||
b<!UNSAFE_CALL!>.<!>inc()
|
||||
@@ -111,7 +111,7 @@ fun test_9(a: Int, b: Int?) {
|
||||
}
|
||||
b<!UNSAFE_CALL!>.<!>inc()
|
||||
|
||||
if (b === a) {
|
||||
if (<!IMPLICIT_BOXING_IN_IDENTITY_EQUALS!>b === a<!>) {
|
||||
b.inc()
|
||||
}
|
||||
b<!UNSAFE_CALL!>.<!>inc()
|
||||
|
||||
@@ -22,12 +22,12 @@ enum class Second {
|
||||
val ONE = Second.THREE
|
||||
|
||||
fun foo(f: First) = <!NO_ELSE_IN_WHEN!>when<!> (f) {
|
||||
<!INCOMPATIBLE_TYPES!>ONE<!> -> 1
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>ONE<!> -> 1
|
||||
TWO -> 2
|
||||
}
|
||||
|
||||
fun bar(s: Second) = <!NO_ELSE_IN_WHEN!>when<!> (s) {
|
||||
<!INCOMPATIBLE_TYPES!>THREE<!> -> 3
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>THREE<!> -> 3
|
||||
FOUR -> 4
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* UNEXPECTED BEHAVIOUR
|
||||
* ISSUES: KT-37081
|
||||
|
||||
Vendored
+2
-1
@@ -1,8 +1,9 @@
|
||||
// FULL_JDK
|
||||
// ISSUE: KT-48113
|
||||
// STATUS: On a K2 technical meeting on Mar-27-2023 it was decided to report a warning
|
||||
// WITH_EXTENDED_CHECKERS
|
||||
|
||||
fun collapse(path: String) {
|
||||
val result = (path as <!PLATFORM_CLASS_MAPPED_TO_KOTLIN!>java.lang.String<!>).replace("123", "456")
|
||||
if (result !== path) {}
|
||||
if (<!EQUALITY_NOT_APPLICABLE_WARNING!>result !== path<!>) {}
|
||||
}
|
||||
|
||||
+90
@@ -159,6 +159,24 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/CompareToWithErrorType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingArbitraryClasses.kt")
|
||||
public void testComparingArbitraryClasses() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/comparingArbitraryClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingCallableReferencesWithInstanceOfJavaClass.kt")
|
||||
public void testComparingCallableReferencesWithInstanceOfJavaClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/comparingCallableReferencesWithInstanceOfJavaClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparisonOfGenericInterfaceWithGenericClass.kt")
|
||||
public void testComparisonOfGenericInterfaceWithGenericClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/comparisonOfGenericInterfaceWithGenericClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("Constants.kt")
|
||||
public void testConstants() throws Exception {
|
||||
@@ -243,6 +261,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/DiamondProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("differentNumericTypesFromSmartCast.kt")
|
||||
public void testDifferentNumericTypesFromSmartCast() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/differentNumericTypesFromSmartCast.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("Dollar.kt")
|
||||
public void testDollar() throws Exception {
|
||||
@@ -255,6 +279,18 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/EnumEntryAsType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityComparisonToSelf.kt")
|
||||
public void testEqualityComparisonToSelf() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/equalityComparisonToSelf.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityWithSmartCastInIfBlock.kt")
|
||||
public void testEqualityWithSmartCastInIfBlock() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/equalityWithSmartCastInIfBlock.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ExtensionCallInvoke.kt")
|
||||
public void testExtensionCallInvoke() throws Exception {
|
||||
@@ -10392,6 +10428,18 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/enum/enumWithEmptyName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityOfEnumAndParameter.kt")
|
||||
public void testEqualityOfEnumAndParameter() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/enum/equalityOfEnumAndParameter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityOfFlexibleTypeParameters.kt")
|
||||
public void testEqualityOfFlexibleTypeParameters() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/enum/equalityOfFlexibleTypeParameters.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ExplicitConstructorCall.kt")
|
||||
public void testExplicitConstructorCall() throws Exception {
|
||||
@@ -35359,6 +35407,30 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/commonCollections.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingDifferentSubclassesCommonInterface.kt")
|
||||
public void testComparingDifferentSubclassesCommonInterface() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/comparingDifferentSubclassesCommonInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingPlatformTypes.kt")
|
||||
public void testComparingPlatformTypes() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/comparingPlatformTypes.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingSmartCastValueToBoolean.kt")
|
||||
public void testComparingSmartCastValueToBoolean() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/comparingSmartCastValueToBoolean.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingTripleWithPair.kt")
|
||||
public void testComparingTripleWithPair() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/comparingTripleWithPair.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("compileTimeUnsignedArray.kt")
|
||||
public void testCompileTimeUnsignedArray() throws Exception {
|
||||
@@ -35383,6 +35455,24 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/enumEntryInitialization.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityCompatibilityCommonCases.kt")
|
||||
public void testEqualityCompatibilityCommonCases() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/equalityCompatibilityCommonCases.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityCompatibilityOldBehavior_Off.kt")
|
||||
public void testEqualityCompatibilityOldBehavior_Off() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/equalityCompatibilityOldBehavior_Off.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityCompatibilityOldBehavior_On.kt")
|
||||
public void testEqualityCompatibilityOldBehavior_On() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/equalityCompatibilityOldBehavior_On.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exitProcess.kt")
|
||||
public void testExitProcess() throws Exception {
|
||||
|
||||
+90
@@ -159,6 +159,24 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/CompareToWithErrorType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingArbitraryClasses.kt")
|
||||
public void testComparingArbitraryClasses() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/comparingArbitraryClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingCallableReferencesWithInstanceOfJavaClass.kt")
|
||||
public void testComparingCallableReferencesWithInstanceOfJavaClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/comparingCallableReferencesWithInstanceOfJavaClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparisonOfGenericInterfaceWithGenericClass.kt")
|
||||
public void testComparisonOfGenericInterfaceWithGenericClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/comparisonOfGenericInterfaceWithGenericClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("Constants.kt")
|
||||
public void testConstants() throws Exception {
|
||||
@@ -243,6 +261,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/DiamondProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("differentNumericTypesFromSmartCast.kt")
|
||||
public void testDifferentNumericTypesFromSmartCast() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/differentNumericTypesFromSmartCast.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("Dollar.kt")
|
||||
public void testDollar() throws Exception {
|
||||
@@ -255,6 +279,18 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/EnumEntryAsType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityComparisonToSelf.kt")
|
||||
public void testEqualityComparisonToSelf() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/equalityComparisonToSelf.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityWithSmartCastInIfBlock.kt")
|
||||
public void testEqualityWithSmartCastInIfBlock() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/equalityWithSmartCastInIfBlock.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ExtensionCallInvoke.kt")
|
||||
public void testExtensionCallInvoke() throws Exception {
|
||||
@@ -10398,6 +10434,18 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/enum/enumWithEmptyName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityOfEnumAndParameter.kt")
|
||||
public void testEqualityOfEnumAndParameter() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/enum/equalityOfEnumAndParameter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityOfFlexibleTypeParameters.kt")
|
||||
public void testEqualityOfFlexibleTypeParameters() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/enum/equalityOfFlexibleTypeParameters.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ExplicitConstructorCall.kt")
|
||||
public void testExplicitConstructorCall() throws Exception {
|
||||
@@ -35455,6 +35503,30 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/commonCollections.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingDifferentSubclassesCommonInterface.kt")
|
||||
public void testComparingDifferentSubclassesCommonInterface() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/comparingDifferentSubclassesCommonInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingPlatformTypes.kt")
|
||||
public void testComparingPlatformTypes() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/comparingPlatformTypes.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingSmartCastValueToBoolean.kt")
|
||||
public void testComparingSmartCastValueToBoolean() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/comparingSmartCastValueToBoolean.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("comparingTripleWithPair.kt")
|
||||
public void testComparingTripleWithPair() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/comparingTripleWithPair.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("compileTimeUnsignedArray.kt")
|
||||
public void testCompileTimeUnsignedArray() throws Exception {
|
||||
@@ -35479,6 +35551,24 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/enumEntryInitialization.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityCompatibilityCommonCases.kt")
|
||||
public void testEqualityCompatibilityCommonCases() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/equalityCompatibilityCommonCases.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityCompatibilityOldBehavior_Off.kt")
|
||||
public void testEqualityCompatibilityOldBehavior_Off() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/equalityCompatibilityOldBehavior_Off.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalityCompatibilityOldBehavior_On.kt")
|
||||
public void testEqualityCompatibilityOldBehavior_On() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/equalityCompatibilityOldBehavior_On.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exitProcess.kt")
|
||||
public void testExitProcess() throws Exception {
|
||||
|
||||
+20
@@ -1338,6 +1338,26 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
val INCOMPATIBLE_ENUM_COMPARISON by warning<KtElement> {
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
val FORBIDDEN_IDENTITY_EQUALS by error<KtElement> {
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
val FORBIDDEN_IDENTITY_EQUALS_WARNING by warning<KtElement> {
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
val DEPRECATED_IDENTITY_EQUALS by warning<KtElement> {
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
val IMPLICIT_BOXING_IN_IDENTITY_EQUALS by warning<KtElement> {
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
val INC_DEC_SHOULD_NOT_RETURN_UNIT by error<KtExpression>(PositioningStrategy.OPERATOR)
|
||||
val ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT by error<KtExpression>(PositioningStrategy.OPERATOR) {
|
||||
parameter<FirNamedFunctionSymbol>("functionSymbol")
|
||||
|
||||
@@ -693,6 +693,11 @@ object FirErrors {
|
||||
val EQUALITY_NOT_APPLICABLE by error3<KtBinaryExpression, String, ConeKotlinType, ConeKotlinType>()
|
||||
val EQUALITY_NOT_APPLICABLE_WARNING by warning3<KtBinaryExpression, String, ConeKotlinType, ConeKotlinType>()
|
||||
val INCOMPATIBLE_ENUM_COMPARISON_ERROR by error2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||
val INCOMPATIBLE_ENUM_COMPARISON by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||
val FORBIDDEN_IDENTITY_EQUALS by error2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||
val FORBIDDEN_IDENTITY_EQUALS_WARNING by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||
val DEPRECATED_IDENTITY_EQUALS by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||
val IMPLICIT_BOXING_IN_IDENTITY_EQUALS by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||
val INC_DEC_SHOULD_NOT_RETURN_UNIT by error0<KtExpression>(SourceElementPositioningStrategies.OPERATOR)
|
||||
val ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT by error2<KtExpression, FirNamedFunctionSymbol, String>(SourceElementPositioningStrategies.OPERATOR)
|
||||
val PROPERTY_AS_OPERATOR by error1<PsiElement, FirPropertySymbol>(SourceElementPositioningStrategies.OPERATOR)
|
||||
|
||||
+299
-81
@@ -5,109 +5,247 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.KtRealSourceElementKind
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.ConeTypeCompatibilityChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.ConeTypeCompatibilityChecker.isCompatible
|
||||
import org.jetbrains.kotlin.KtRealSourceElementKind
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory2
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.collectUpperBounds
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isInline
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
|
||||
import org.jetbrains.kotlin.fir.expressions.FirEqualityOperatorCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirOperation
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
|
||||
import org.jetbrains.kotlin.fir.isPrimitiveType
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.text
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||
|
||||
object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
|
||||
override fun check(expression: FirEqualityOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val arguments = expression.argumentList.arguments
|
||||
if (arguments.size != 2) return
|
||||
val lType = arguments[0].typeRef.coneType
|
||||
val rType = arguments[1].typeRef.coneType
|
||||
checkCompatibility(lType, rType, context, expression, reporter)
|
||||
checkSensibleness(lType, rType, context, expression, reporter)
|
||||
}
|
||||
require(arguments.size == 2) { "Equality operator call with non-2 arguments" }
|
||||
|
||||
private fun checkCompatibility(
|
||||
lType: ConeKotlinType,
|
||||
rType: ConeKotlinType,
|
||||
context: CheckerContext,
|
||||
expression: FirEqualityOperatorCall,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
// If one of the type is already `Nothing?`, we skip reporting further comparison. This is to allow comparing with `null`, which has
|
||||
// type `Nothing?`
|
||||
if (lType.isNullableNothing || rType.isNullableNothing) return
|
||||
val inferenceContext = context.session.typeContext
|
||||
val l = arguments[0].toArgumentInfo(context)
|
||||
val r = arguments[1].toArgumentInfo(context)
|
||||
|
||||
val compatibility = try {
|
||||
inferenceContext.isCompatible(lType, rType)
|
||||
} catch (e: Throwable) {
|
||||
throw IllegalStateException(
|
||||
"Exception while determining type compatibility: lType: $lType, rType: $rType, " +
|
||||
"equality ${expression.render()}, " +
|
||||
"file ${context.containingFile?.name}",
|
||||
e
|
||||
checkSenselessness(l.smartCastType, r.smartCastType, context, expression, reporter)
|
||||
|
||||
val checkApplicability = when (expression.operation) {
|
||||
FirOperation.EQ, FirOperation.NOT_EQ -> ::checkEqualityApplicability
|
||||
FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> ::checkIdentityApplicability
|
||||
else -> error("Invalid operator of FirEqualityOperatorCall")
|
||||
}
|
||||
|
||||
checkApplicability(l.originalTypeInfo, r.originalTypeInfo, context).ifInapplicable {
|
||||
// Ideally this should match cases when K1
|
||||
// sees a non-empty intersection and none of the
|
||||
// types is an enum, but intersections in K1
|
||||
// work differently from intersections in K2.
|
||||
val isCaseMissedByK1 = it != Applicability.INAPPLICABLE_AS_ENUMS
|
||||
&& l.originalTypeInfo.isLiterallyTypeParameter
|
||||
&& r.originalTypeInfo.isLiterallyTypeParameter
|
||||
val replicateK1Behavior = !context.languageVersionSettings.supportsFeature(LanguageFeature.ReportErrorsForComparisonOperators)
|
||||
|
||||
return reporter.reportInapplicabilityDiagnostic(
|
||||
expression, it, expression.operation, forceWarning = isCaseMissedByK1 && replicateK1Behavior,
|
||||
l.originalTypeInfo, r.originalTypeInfo,
|
||||
l.userType, r.userType, context,
|
||||
)
|
||||
}
|
||||
if (compatibility != ConeTypeCompatibilityChecker.Compatibility.COMPATIBLE) {
|
||||
when (expression.source?.kind) {
|
||||
KtRealSourceElementKind -> {
|
||||
// Note: FE1.0 reports INCOMPATIBLE_ENUM_COMPARISON_ERROR only when TypeIntersector.isIntersectionEmpty() thinks the
|
||||
// given types are compatible. Exactly mimicking the behavior of FE1.0 is difficult and does not seem to provide any
|
||||
// value. So instead, we deterministically output INCOMPATIBLE_ENUM_COMPARISON_ERROR if at least one of the value is an
|
||||
// enum.
|
||||
if (compatibility == ConeTypeCompatibilityChecker.Compatibility.HARD_INCOMPATIBLE &&
|
||||
(lType.isEnumType(context) || rType.isEnumType(context))
|
||||
) {
|
||||
reporter.reportOn(
|
||||
expression.source,
|
||||
FirErrors.INCOMPATIBLE_ENUM_COMPARISON_ERROR,
|
||||
lType,
|
||||
rType,
|
||||
context
|
||||
)
|
||||
} else {
|
||||
reporter.reportOn(
|
||||
expression.source,
|
||||
if (compatibility == ConeTypeCompatibilityChecker.Compatibility.HARD_INCOMPATIBLE) {
|
||||
FirErrors.EQUALITY_NOT_APPLICABLE
|
||||
} else {
|
||||
FirErrors.EQUALITY_NOT_APPLICABLE_WARNING
|
||||
},
|
||||
expression.operation.operator,
|
||||
lType,
|
||||
rType,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> reporter.reportOn(
|
||||
expression.source,
|
||||
if (compatibility == ConeTypeCompatibilityChecker.Compatibility.HARD_INCOMPATIBLE) {
|
||||
FirErrors.INCOMPATIBLE_TYPES
|
||||
} else {
|
||||
FirErrors.INCOMPATIBLE_TYPES_WARNING
|
||||
},
|
||||
lType,
|
||||
rType,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
if (l.argument !is FirSmartCastExpression && r.argument !is FirSmartCastExpression) {
|
||||
return
|
||||
}
|
||||
|
||||
checkApplicability(l.smartCastTypeInfo, r.smartCastTypeInfo, context).ifInapplicable {
|
||||
return reporter.reportInapplicabilityDiagnostic(
|
||||
expression, it, expression.operation, forceWarning = true,
|
||||
l.smartCastTypeInfo, r.smartCastTypeInfo,
|
||||
l.userType, r.userType, context,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.isEnumType(
|
||||
context: CheckerContext
|
||||
): Boolean {
|
||||
if (isEnum) return true
|
||||
val firRegularClassSymbol = (this as? ConeClassLikeType)?.lookupTag?.toFirRegularClassSymbol(context.session) ?: return false
|
||||
return firRegularClassSymbol.isEnumClass
|
||||
private fun checkEqualityApplicability(l: TypeInfo, r: TypeInfo, context: CheckerContext): Applicability {
|
||||
val oneIsBuiltin = l.isBuiltin || r.isBuiltin
|
||||
|
||||
// The compiler should only check comparisons
|
||||
// when builtins are involved.
|
||||
// Builtins' supertypes must not be present in
|
||||
// the list of special fqNames described in RULES1
|
||||
|
||||
return when {
|
||||
oneIsBuiltin && shouldReportAsPerRules1(l, r, context) -> getInapplicabilityFor(l, r)
|
||||
else -> Applicability.APPLICABLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSensibleness(
|
||||
private fun checkIdentityApplicability(l: TypeInfo, r: TypeInfo, context: CheckerContext): Applicability {
|
||||
// The compiler should only check comparisons
|
||||
// when identity-less types or builtins are involved.
|
||||
|
||||
val oneIsBuiltin = l.isBuiltin || r.isBuiltin
|
||||
val oneIsNotNull = !l.type.isNullable || !r.type.isNullable
|
||||
|
||||
return when {
|
||||
l.isIdentityLess || r.isIdentityLess -> Applicability.INAPPLICABLE_AS_IDENTITY_LESS
|
||||
oneIsBuiltin && oneIsNotNull && shouldReportAsPerRules1(l, r, context) -> getInapplicabilityFor(l, r)
|
||||
else -> Applicability.APPLICABLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInapplicabilityFor(l: TypeInfo, r: TypeInfo): Applicability {
|
||||
val isIntersectionEmpty = l.enforcesEmptyIntersection || r.enforcesEmptyIntersection
|
||||
val isOneEnum = l.isEnumClass || r.isEnumClass
|
||||
|
||||
return when {
|
||||
!isIntersectionEmpty && isOneEnum -> Applicability.INAPPLICABLE_AS_ENUMS
|
||||
else -> Applicability.GENERALLY_INAPPLICABLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldReportAsPerRules1(l: TypeInfo, r: TypeInfo, context: CheckerContext): Boolean {
|
||||
// Builtins are always final classes, so
|
||||
// we only need to check if one is related
|
||||
// to the other
|
||||
|
||||
fun TypeInfo.isSubclassOf(other: TypeInfo) = when {
|
||||
other.enforcesEmptyIntersection -> type.classId == other.type.classId
|
||||
else -> notNullType.isSubtypeOf(other.notNullType, context.session)
|
||||
}
|
||||
|
||||
return when {
|
||||
l.type.isNothingOrNullableNothing || r.type.isNothingOrNullableNothing -> false
|
||||
else -> !l.isSubclassOf(r) && !r.isSubclassOf(l)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Applicability {
|
||||
APPLICABLE,
|
||||
GENERALLY_INAPPLICABLE,
|
||||
INAPPLICABLE_AS_ENUMS,
|
||||
INAPPLICABLE_AS_IDENTITY_LESS,
|
||||
}
|
||||
|
||||
private inline fun Applicability.ifInapplicable(block: (Applicability) -> Unit) = when (this) {
|
||||
Applicability.APPLICABLE -> {}
|
||||
else -> block(this)
|
||||
}
|
||||
|
||||
private fun getGeneralInapplicabilityDiagnostic(forceWarning: Boolean) = when {
|
||||
forceWarning -> FirErrors.EQUALITY_NOT_APPLICABLE_WARNING
|
||||
else -> FirErrors.EQUALITY_NOT_APPLICABLE
|
||||
}
|
||||
|
||||
private fun getIdentityLessInapplicabilityDiagnostic(
|
||||
l: TypeInfo,
|
||||
r: TypeInfo,
|
||||
forceWarning: Boolean,
|
||||
context: CheckerContext,
|
||||
): KtDiagnosticFactory2<ConeKotlinType, ConeKotlinType> {
|
||||
val areBothPrimitives = l.isPrimitive && r.isPrimitive
|
||||
val areSameTypes = l.type.classId == r.type.classId
|
||||
val shouldProperlyReportError = context.languageVersionSettings.supportsFeature(LanguageFeature.ReportErrorsForComparisonOperators)
|
||||
|
||||
// In this case K1 reports nothing
|
||||
val shouldRelaxDiagnostic = (l.type.isNullableNothing || r.type.isNullableNothing) && !shouldProperlyReportError
|
||||
|
||||
return when {
|
||||
// See: KT-28252
|
||||
areSameTypes && areBothPrimitives -> FirErrors.DEPRECATED_IDENTITY_EQUALS
|
||||
// The same reason as above
|
||||
isIdentityComparedWithImplicitBoxing(l, r, context.session) -> FirErrors.IMPLICIT_BOXING_IN_IDENTITY_EQUALS
|
||||
forceWarning || shouldRelaxDiagnostic -> FirErrors.FORBIDDEN_IDENTITY_EQUALS_WARNING
|
||||
else -> FirErrors.FORBIDDEN_IDENTITY_EQUALS
|
||||
}
|
||||
}
|
||||
|
||||
private fun isIdentityComparedWithImplicitBoxing(l: TypeInfo, r: TypeInfo, session: FirSession) =
|
||||
isPrimitiveWithNonPrimitiveSupertype(l, r, session) || isPrimitiveWithNonPrimitiveSupertype(r, l, session)
|
||||
|
||||
private fun isPrimitiveWithNonPrimitiveSupertype(l: TypeInfo, r: TypeInfo, session: FirSession) =
|
||||
l.isPrimitive && !r.isPrimitive && l.type.isSubtypeOf(r.type, session)
|
||||
|
||||
private fun getSourceLessInapplicabilityDiagnostic(forceWarning: Boolean) = when {
|
||||
forceWarning -> FirErrors.INCOMPATIBLE_TYPES_WARNING
|
||||
else -> FirErrors.INCOMPATIBLE_TYPES
|
||||
}
|
||||
|
||||
private fun getEnumInapplicabilityDiagnostic(
|
||||
l: TypeInfo,
|
||||
r: TypeInfo,
|
||||
forceWarning: Boolean,
|
||||
context: CheckerContext,
|
||||
): KtDiagnosticFactory2<ConeKotlinType, ConeKotlinType> {
|
||||
// Preserving the behavior on the old test data
|
||||
// simplifies detecting fir-differences,
|
||||
// which is crucial for this checker
|
||||
val isOldTestData = !context.languageVersionSettings.supportsFeature(
|
||||
LanguageFeature.ProhibitComparisonOfIncompatibleEnums,
|
||||
)
|
||||
|
||||
// In this corner case K1 reports nothing
|
||||
val bothNullableEnums = l.isNullableEnum && r.isNullableEnum
|
||||
// When comparing enums, for type parameters K1
|
||||
// tries to pick the "representative" superclass
|
||||
// instead of the proper intersection.
|
||||
// We can't guarantee that in such cases
|
||||
// K2 reports the same as K1
|
||||
val areIntersectionsInvolved = l.type is ConeIntersectionType || r.type is ConeIntersectionType
|
||||
|
||||
val shouldProperlyReportError = context.languageVersionSettings.supportsFeature(LanguageFeature.ReportErrorsForComparisonOperators)
|
||||
val shouldRelaxDiagnostic = (bothNullableEnums || areIntersectionsInvolved) && !shouldProperlyReportError
|
||||
|
||||
return when {
|
||||
forceWarning || isOldTestData || shouldRelaxDiagnostic -> FirErrors.INCOMPATIBLE_ENUM_COMPARISON
|
||||
else -> FirErrors.INCOMPATIBLE_ENUM_COMPARISON_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
private fun DiagnosticReporter.reportInapplicabilityDiagnostic(
|
||||
expression: FirEqualityOperatorCall,
|
||||
applicability: Applicability,
|
||||
operation: FirOperation,
|
||||
forceWarning: Boolean,
|
||||
l: TypeInfo,
|
||||
r: TypeInfo,
|
||||
lUserType: ConeKotlinType,
|
||||
rUserType: ConeKotlinType,
|
||||
context: CheckerContext,
|
||||
): Unit = when {
|
||||
applicability == Applicability.INAPPLICABLE_AS_IDENTITY_LESS -> reportOn(
|
||||
expression.source, getIdentityLessInapplicabilityDiagnostic(l, r, forceWarning, context),
|
||||
lUserType, rUserType, context,
|
||||
)
|
||||
applicability == Applicability.INAPPLICABLE_AS_ENUMS -> reportOn(
|
||||
expression.source, getEnumInapplicabilityDiagnostic(l, r, forceWarning, context),
|
||||
lUserType, rUserType, context,
|
||||
)
|
||||
// This check ensures K2 reports the same diagnostics as K1 used to.
|
||||
expression.source?.kind !is KtRealSourceElementKind -> reportOn(
|
||||
expression.source, getSourceLessInapplicabilityDiagnostic(forceWarning),
|
||||
lUserType, rUserType, context,
|
||||
)
|
||||
applicability == Applicability.GENERALLY_INAPPLICABLE -> reportOn(
|
||||
expression.source, getGeneralInapplicabilityDiagnostic(forceWarning),
|
||||
operation.operator, lUserType, rUserType, context,
|
||||
)
|
||||
else -> error("Shouldn't be here")
|
||||
}
|
||||
|
||||
private fun checkSenselessness(
|
||||
lType: ConeKotlinType,
|
||||
rType: ConeKotlinType,
|
||||
context: CheckerContext,
|
||||
@@ -139,3 +277,83 @@ object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TypeInfo(
|
||||
val type: ConeKotlinType,
|
||||
val notNullType: ConeKotlinType,
|
||||
val isFinalClass: Boolean,
|
||||
val isEnumClass: Boolean,
|
||||
val isPrimitive: Boolean,
|
||||
val isBuiltin: Boolean,
|
||||
val isValueClass: Boolean,
|
||||
val isLiterallyTypeParameter: Boolean,
|
||||
) {
|
||||
override fun toString() = "$type"
|
||||
}
|
||||
|
||||
private val FirClassSymbol<*>.isBuiltin get() = isPrimitiveType() || classId == StandardClassIds.String || isEnumClass
|
||||
|
||||
// This property is used to replicate K1 behavior, and it
|
||||
// tries to predict empty intersections from the K1 point-of-view.
|
||||
// Enum classes are final, but enum entries are their subclasses.
|
||||
private val TypeInfo.enforcesEmptyIntersection get() = isFinalClass && !isEnumClass
|
||||
|
||||
private val TypeInfo.isNullableEnum get() = isEnumClass && type.isNullable
|
||||
|
||||
private val TypeInfo.isIdentityLess get() = isPrimitive || !type.isNullable && isValueClass
|
||||
|
||||
private val FirClassSymbol<*>.isFinalClass get() = isClass && isFinal
|
||||
|
||||
// NB: This is what RULES1 means then it says "class".
|
||||
private val FirClassSymbol<*>.isClass get() = !isInterface
|
||||
|
||||
private fun ConeKotlinType.toTypeInfo(session: FirSession): TypeInfo {
|
||||
val bounds = collectUpperBounds().map { type -> toKotlinType(type).replaceArgumentsWithStarProjections() }
|
||||
val type = bounds.ifNotEmpty { ConeTypeIntersector.intersectTypes(session.typeContext, this) }
|
||||
?: session.builtinTypes.nullableAnyType.type
|
||||
val notNullType = type.withNullability(ConeNullability.NOT_NULL, session.typeContext)
|
||||
|
||||
return TypeInfo(
|
||||
type, notNullType,
|
||||
isFinalClass = bounds.any { it.toClassSymbol(session)?.isFinalClass == true },
|
||||
isEnumClass = bounds.any { it.toClassSymbol(session)?.isEnumClass == true },
|
||||
isPrimitive = bounds.any { it.isPrimitive },
|
||||
isBuiltin = bounds.any { it.toClassSymbol(session)?.isBuiltin == true },
|
||||
isValueClass = bounds.any { it.toClassSymbol(session)?.isInline == true },
|
||||
isLiterallyTypeParameter = this.lowerBoundIfFlexible() is ConeTypeParameterType,
|
||||
)
|
||||
}
|
||||
|
||||
private fun toKotlinType(type: ConeClassLikeType): ConeClassLikeType {
|
||||
// Type arguments are ignored by design
|
||||
return ConeClassLikeTypeImpl(type.lookupTag, type.typeArguments, type.isNullable)
|
||||
}
|
||||
|
||||
private class ArgumentInfo(
|
||||
val argument: FirExpression,
|
||||
val userType: ConeKotlinType,
|
||||
val originalType: ConeKotlinType,
|
||||
val session: FirSession,
|
||||
) {
|
||||
val smartCastType: ConeKotlinType by lazy {
|
||||
if (argument !is FirSmartCastExpression) originalType else userType.fullyExpandedType(session)
|
||||
}
|
||||
|
||||
val originalTypeInfo get() = originalType.toTypeInfo(session)
|
||||
|
||||
val smartCastTypeInfo get() = smartCastType.toTypeInfo(session)
|
||||
|
||||
override fun toString() = "${argument.source?.text} :: $userType"
|
||||
}
|
||||
|
||||
@Suppress("RecursivePropertyAccessor")
|
||||
private val FirExpression.mostOriginalTypeIfSmartCast: ConeKotlinType
|
||||
get() = when (this) {
|
||||
is FirSmartCastExpression -> originalExpression.mostOriginalTypeIfSmartCast
|
||||
else -> typeRef.coneType
|
||||
}
|
||||
|
||||
private fun FirExpression.toArgumentInfo(context: CheckerContext) =
|
||||
ArgumentInfo(
|
||||
this, typeRef.coneType, mostOriginalTypeIfSmartCast.fullyExpandedType(context.session), context.session,
|
||||
)
|
||||
|
||||
+35
@@ -221,7 +221,10 @@ 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.EXPOSED_PROPERTY_TYPE_IN_CONSTRUCTOR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_IDENTITY_EQUALS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_BINARY_MOD
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_IDENTITY_EQUALS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_IDENTITY_EQUALS_WARNING
|
||||
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
|
||||
@@ -249,6 +252,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_SUSPEND_F
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_SUSPEND_PROPERTY_ACCESS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_UNDERSCORE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IMPLICIT_BOXING_IN_IDENTITY_EQUALS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IMPLICIT_NOTHING_PROPERTY_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IMPLICIT_NOTHING_RETURN_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_CANDIDATE
|
||||
@@ -262,6 +266,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_TARG
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_DELEGATE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_ENUM_COMPARISON
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_ENUM_COMPARISON_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_MODIFIERS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPES
|
||||
@@ -2002,6 +2007,36 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
RENDER_TYPE,
|
||||
RENDER_TYPE
|
||||
)
|
||||
map.put(
|
||||
INCOMPATIBLE_ENUM_COMPARISON,
|
||||
"Comparison of incompatible enums ''{0}'' and ''{1}'' is always unsuccessful",
|
||||
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(
|
||||
FORBIDDEN_IDENTITY_EQUALS_WARNING,
|
||||
"Identity equality for arguments of types {0} and {1} is forbidden",
|
||||
RENDER_TYPE,
|
||||
RENDER_TYPE
|
||||
)
|
||||
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(INC_DEC_SHOULD_NOT_RETURN_UNIT, "Functions inc(), dec() shouldn't return Unit to be used by operators ++, --")
|
||||
map.put(
|
||||
ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT,
|
||||
|
||||
@@ -23,10 +23,7 @@ import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
@@ -607,6 +604,10 @@ fun ConeKotlinType.toRegularClassSymbol(session: FirSession): FirRegularClassSym
|
||||
return (this as? ConeClassLikeType)?.toRegularClassSymbol(session)
|
||||
}
|
||||
|
||||
fun ConeClassLikeType.toClassSymbol(session: FirSession): FirClassSymbol<*>? {
|
||||
return fullyExpandedType(session).toSymbol(session) as? FirClassSymbol<*>
|
||||
}
|
||||
|
||||
private fun lowerThanBound(context: ConeInferenceContext, argument: ConeKotlinType, typeParameterSymbol: FirTypeParameterSymbol): Boolean {
|
||||
typeParameterSymbol.resolvedBounds.forEach { boundTypeRef ->
|
||||
if (argument != boundTypeRef.coneType && argument.isSubtypeOf(context, boundTypeRef.coneType)) {
|
||||
|
||||
+6
@@ -83,12 +83,18 @@ inline val FirClassSymbol<*>.isLocalClassOrAnonymousObject: Boolean
|
||||
get() = classId.isLocal || this is FirAnonymousObjectSymbol
|
||||
|
||||
|
||||
inline val FirClassSymbol<*>.isClass: Boolean
|
||||
get() = classKind.isClass
|
||||
|
||||
inline val FirClassSymbol<*>.isInterface: Boolean
|
||||
get() = classKind.isInterface
|
||||
|
||||
inline val FirClassSymbol<*>.isEnumClass: Boolean
|
||||
get() = classKind.isEnumClass
|
||||
|
||||
inline val FirClassSymbol<*>.isEnumEntry: Boolean
|
||||
get() = classKind.isEnumEntry
|
||||
|
||||
// ---------------------- specific callables ----------------------
|
||||
|
||||
inline val FirPropertyAccessorSymbol.allowsToHaveFakeOverride: Boolean get() = visibility.allowsToHaveFakeOverride
|
||||
|
||||
Reference in New Issue
Block a user