FIR: add equality call checker

Added checker for FirEqualityOperatorCall. It's surfaced as one of the
following diagnostics depending on the PSI structure and types under
comparison:

* INCOMPATIBLE_TYPES(_WARNING)
* EQUALITY_NOT_APPLICABLE(_WARNING)
* INCOMPATIBLE_ENUM_COMPARISON_ERROR

Comparing with FE1.0, the current implementation is more conservative
and only highlights error if the types are known to follow certain
contracts with `equals` method. Otherwise, the checker reports warnings
instead.

However, the current checker is more strict in the following situations:
1. it now rejects incompatible enum types like `Enum<E1>` and
  `Enum<E2>`, which was previously accepted
2. it now rejects incompatible class types like `Class<String>` and
  `Class<Int>`, which was previously accepted
3. the check now takes smart cast into consideration, so
  `if (x is String) x == 3` is now rejected
This commit is contained in:
Tianyu Geng
2021-04-02 20:49:50 -07:00
committed by teamcityserver
parent 787c743333
commit 7bb81ef157
43 changed files with 1218 additions and 188 deletions
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.classId
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds
object PrimitiveTypes {
val Boolean: ConeClassLikeType = StandardClassIds.Boolean.createType()
@@ -34,11 +36,27 @@ fun ConeClassLikeType.isByte(): Boolean = lookupTag.classId == StandardClassIds.
fun ConeClassLikeType.isBoolean(): Boolean = lookupTag.classId == StandardClassIds.Boolean
fun ConeClassLikeType.isChar(): Boolean = lookupTag.classId == StandardClassIds.Char
fun ConeClassLikeType.isPrimitiveType(): Boolean = isPrimitiveNumberOrUnsignedNumberType() || isBoolean() || isByte() || isShort() || isChar()
fun ConeClassLikeType.isPrimitiveType(): Boolean =
isPrimitiveNumberOrUnsignedNumberType() || isBoolean() || isByte() || isShort() || isChar()
fun ConeClassLikeType.isPrimitiveNumberType(): Boolean = lookupTag.classId in PRIMITIVE_NUMBER_CLASS_IDS
fun ConeClassLikeType.isPrimitiveUnsignedNumberType(): Boolean = lookupTag.classId in PRIMITIVE_UNSIGNED_NUMBER_CLASS_IDS
fun ConeClassLikeType.isPrimitiveNumberOrUnsignedNumberType(): Boolean = isPrimitiveNumberType() || isPrimitiveUnsignedNumberType()
fun FirClass<*>.isDouble(): Boolean = classId == StandardClassIds.Double
fun FirClass<*>.isFloat(): Boolean = classId == StandardClassIds.Float
fun FirClass<*>.isLong(): Boolean = classId == StandardClassIds.Long
fun FirClass<*>.isInt(): Boolean = classId == StandardClassIds.Int
fun FirClass<*>.isShort(): Boolean = classId == StandardClassIds.Short
fun FirClass<*>.isByte(): Boolean = classId == StandardClassIds.Byte
fun FirClass<*>.isBoolean(): Boolean = classId == StandardClassIds.Boolean
fun FirClass<*>.isChar(): Boolean = classId == StandardClassIds.Char
fun FirClass<*>.isPrimitiveType(): Boolean = isPrimitiveNumberOrUnsignedNumberType() || isBoolean() || isByte() || isShort() || isChar()
fun FirClass<*>.isPrimitiveNumberType(): Boolean = classId in PRIMITIVE_NUMBER_CLASS_IDS
fun FirClass<*>.isPrimitiveUnsignedNumberType(): Boolean = classId in PRIMITIVE_UNSIGNED_NUMBER_CLASS_IDS
fun FirClass<*>.isPrimitiveNumberOrUnsignedNumberType(): Boolean = isPrimitiveNumberType() || isPrimitiveUnsignedNumberType()
private val PRIMITIVE_NUMBER_CLASS_IDS: Set<ClassId> = setOf(
StandardClassIds.Double, StandardClassIds.Float, StandardClassIds.Long, StandardClassIds.Int,
StandardClassIds.Short, StandardClassIds.Byte
@@ -35,10 +35,10 @@ fun FirTypeParameterBuilder.addDefaultBoundIfNecessary(isFlexible: Boolean = fal
}
}
inline val FirRegularClass.isInterface: Boolean
inline val FirClass<*>.isInterface: Boolean
get() = classKind == ClassKind.INTERFACE
inline val FirRegularClass.isEnumClass: Boolean
inline val FirClass<*>.isEnumClass: Boolean
get() = classKind == ClassKind.ENUM_CLASS
inline val FirRegularClass.modality get() = status.modality