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:
committed by
teamcityserver
parent
787c743333
commit
7bb81ef157
+1
-1
@@ -32,7 +32,7 @@ fun case1(javaClass: JavaClass?) {
|
||||
}
|
||||
|
||||
class Case1(val javaClass: JavaClass?) {
|
||||
val x = if (javaClass != null) { it -> it == javaClass } else BooCase2.FILTER
|
||||
val x = if (javaClass != null) { it -> <!EQUALITY_NOT_APPLICABLE_WARNING!>it == javaClass<!> } else BooCase2.FILTER
|
||||
}
|
||||
|
||||
class BooCase1() {
|
||||
|
||||
@@ -45,12 +45,12 @@ fun case3() {
|
||||
|
||||
val flag = "" //A
|
||||
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) {// should be NO_ELSE_IN_WHEN
|
||||
A.A1 -> B() //should be INCOMPATIBLE_TYPES
|
||||
A.A2 -> B() //should be INCOMPATIBLE_TYPES
|
||||
<!INCOMPATIBLE_TYPES!>A.A1<!> -> B() //should be INCOMPATIBLE_TYPES
|
||||
<!INCOMPATIBLE_TYPES!>A.A2<!> -> B() //should be INCOMPATIBLE_TYPES
|
||||
}
|
||||
|
||||
val l2 = <!NO_ELSE_IN_WHEN!>when<!> (flag) {// should be NO_ELSE_IN_WHEN
|
||||
A.A1 -> B() //should be INCOMPATIBLE_TYPES
|
||||
A.A2 -> B() //should be INCOMPATIBLE_TYPES
|
||||
<!INCOMPATIBLE_TYPES!>A.A1<!> -> B() //should be INCOMPATIBLE_TYPES
|
||||
<!INCOMPATIBLE_TYPES!>A.A2<!> -> B() //should be INCOMPATIBLE_TYPES
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -9071,6 +9071,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/enum/starImportNestedClassAndEntries.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeCompatibility.kt")
|
||||
public void testTypeCompatibility() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/enum/typeCompatibility.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeParametersInEnum.kt")
|
||||
public void testTypeParametersInEnum() throws Exception {
|
||||
|
||||
+6
@@ -9071,6 +9071,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/enum/starImportNestedClassAndEntries.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeCompatibility.kt")
|
||||
public void testTypeCompatibility() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/enum/typeCompatibility.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeParametersInEnum.kt")
|
||||
public void testTypeParametersInEnum() throws Exception {
|
||||
|
||||
+25
@@ -360,6 +360,16 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
val MISPLACED_TYPE_PARAMETER_CONSTRAINTS by warning<KtTypeParameter>()
|
||||
|
||||
val DYNAMIC_UPPER_BOUND by error<KtTypeReference>()
|
||||
|
||||
val INCOMPATIBLE_TYPES by error<KtElement> {
|
||||
parameter<ConeKotlinType>("typeA")
|
||||
parameter<ConeKotlinType>("typeB")
|
||||
}
|
||||
|
||||
val INCOMPATIBLE_TYPES_WARNING by warning<KtElement> {
|
||||
parameter<ConeKotlinType>("typeA")
|
||||
parameter<ConeKotlinType>("typeB")
|
||||
}
|
||||
}
|
||||
|
||||
val REFLECTION by object : DiagnosticGroup("Reflection") {
|
||||
@@ -721,6 +731,21 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
|
||||
val UNDERSCORE_IS_RESERVED by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
|
||||
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
|
||||
|
||||
val EQUALITY_NOT_APPLICABLE by error<KtBinaryExpression> {
|
||||
parameter<String>("operator")
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
val EQUALITY_NOT_APPLICABLE_WARNING by warning<KtBinaryExpression> {
|
||||
parameter<String>("operator")
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
val INCOMPATIBLE_ENUM_COMPARISON_ERROR by error<KtElement> {
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
}
|
||||
|
||||
val TYPE_ALIAS by object : DiagnosticGroup("Type alias") {
|
||||
|
||||
@@ -259,6 +259,8 @@ object FirErrors {
|
||||
val DEPRECATED_TYPE_PARAMETER_SYNTAX by error0<KtDeclaration>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
|
||||
val MISPLACED_TYPE_PARAMETER_CONSTRAINTS by warning0<KtTypeParameter>()
|
||||
val DYNAMIC_UPPER_BOUND by error0<KtTypeReference>()
|
||||
val INCOMPATIBLE_TYPES by error2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||
val INCOMPATIBLE_TYPES_WARNING by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||
|
||||
// Reflection
|
||||
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
@@ -423,6 +425,9 @@ object FirErrors {
|
||||
val DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH by error3<KtExpression, String, ConeKotlinType, ConeKotlinType>()
|
||||
val UNDERSCORE_IS_RESERVED by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
|
||||
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
|
||||
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>()
|
||||
|
||||
// Type alias
|
||||
val TOPLEVEL_TYPEALIASES_ONLY by error0<KtTypeAlias>()
|
||||
|
||||
+454
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.isPrimitiveType
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.fullyExpandedClass
|
||||
import org.jetbrains.kotlin.fir.resolve.getSymbolByLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
/**
|
||||
* Checks if a given collection of [ConeKotlinType] are compatible. In other words, the types are compatible if it's possible at all to
|
||||
* define a type that's a subtype of all of the given types. The compatibility of a given set of types concept is closely related to whether
|
||||
* the intersection of these types is inhabited. But it's not identical because 1) one can manually control visibility of constructors and
|
||||
* 2) there can be unused type parameters.
|
||||
*
|
||||
* The compatibility check is done recursively on the given types and all type arguments passed to each corresponding type parameters. For
|
||||
* example, consider the following two types:
|
||||
*
|
||||
* ```
|
||||
* - ArrayList<Set<String>>
|
||||
* - List<HashSet<Int>>
|
||||
* ```
|
||||
*
|
||||
* The checker first checks the base types `ArrayList` and `List`, and it sees no issue since `ArrayList <: List`. Next it checks the type
|
||||
* parameters bound to these base types: `T1` in `ArrayList<T1>` and `T2` in `List<T2>`. For `T1`, there is only one bound type argument
|
||||
* `Set<String>`, so it's good. For `T2`, there are two bound type arguments: `Set<String>` and `HashSet<Int>`. Now the checker recursively
|
||||
* checks whether these two types are compatible. Again, it first checks the base type `Set` and `HashSet`, and it finds no problem since
|
||||
* `HashSet <: Set`. Finally, checks the type arguments `String` and `Int` that are bound to `T` in `Set<T>`. They are incompatible since
|
||||
* `String` and `Int` are unrelated classes.
|
||||
*
|
||||
* The above example only goes over covariant type arguments. For contravariant types, the checker simply checks whether the range formed by
|
||||
* covariant and contravariant bounds is empty. For example, a range like `[Collection, List]` is empty and hence invalid because `List` is
|
||||
* not a super class/interface of `Collection`
|
||||
*/
|
||||
internal object ConeTypeCompatibilityChecker {
|
||||
|
||||
/**
|
||||
* The result returned by [ConeTypeCompatibilityChecker]. Note the order of enum entries matters.
|
||||
*/
|
||||
enum class Compatibility : Comparable<Compatibility> {
|
||||
/** The given types are fully compatible. */
|
||||
COMPATIBLE,
|
||||
|
||||
/** The given types may not be compatible. But the compiler would allow such comparisons. */
|
||||
SOFT_INCOMPATIBLE,
|
||||
|
||||
/**
|
||||
* The given types are definitely incompatible. If the established contracts of Kotlin code are respected, values of the given
|
||||
* types can never be considered equal.
|
||||
*/
|
||||
HARD_INCOMPATIBLE,
|
||||
}
|
||||
|
||||
fun Collection<ConeKotlinType>.areCompatible(ctx: ConeInferenceContext): Compatibility {
|
||||
// If all types are nullable, then `null` makes the given types compatible.
|
||||
if (all { with(ctx) { it.isNullableType() } }) return Compatibility.COMPATIBLE
|
||||
|
||||
// Next can simply focus on the type hierarchy and don't need to worry about nullability.
|
||||
val compatibilityUpperBound = if (this.all { it.isConcreteType() }) {
|
||||
Compatibility.HARD_INCOMPATIBLE
|
||||
} else {
|
||||
// If any type is not concrete, for example, type parameter, we only report warning for incompatible types. This is to stay
|
||||
// compatible with FE1.0.
|
||||
Compatibility.SOFT_INCOMPATIBLE
|
||||
}
|
||||
return ctx.areCompatible(flatMap { it.collectUpperBounds() }.toSet(), emptySet(), compatibilityUpperBound)
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.isConcreteType(): Boolean {
|
||||
return when (this) {
|
||||
is ConeClassLikeType -> true
|
||||
is ConeDefinitelyNotNullType -> original.isConcreteType()
|
||||
is ConeIntersectionType -> intersectedTypes.all { it.isConcreteType() }
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param compatibilityUpperBound the max compatibility result that can be returned by this method. For example, if this is set to
|
||||
* [Compatibility.SOFT_INCOMPATIBLE], then even if the given bounds don't match the hard way (for example, incompatible primitives) the
|
||||
* method should still return [Compatibility.SOFT_INCOMPATIBLE]. This is useful for checking type parameters since we don't want to
|
||||
* dictate what semantics a type parameter may have in user code. In other words, if user wants to compare `MyCustom<out String>` with
|
||||
* `MyCustom<out Int>`, we let them do so since we do not know what class `MyCustom` uses the type parameter for. Empty containers are
|
||||
* another example: `emptyList<Int>() == emptyList<String>()`.
|
||||
*/
|
||||
private fun ConeInferenceContext.areCompatible(
|
||||
upperBounds: Set<ConeClassLikeType>,
|
||||
lowerBounds: Set<ConeClassLikeType>,
|
||||
compatibilityUpperBound: Compatibility
|
||||
): Compatibility {
|
||||
val upperBoundClasses: Set<FirClassWithSuperClasses> = upperBounds.mapNotNull { it.toFirClassWithSuperClasses(this) }.toSet()
|
||||
|
||||
// Following if condition is an optimization: if we ignore the subtyping relation and treat all upper bounds as unrelated
|
||||
// classes/interfaces, yet the types are deemed compatible for sure, then we just bail out early.
|
||||
if (lowerBounds.isEmpty() &&
|
||||
(upperBounds.size < 2 ||
|
||||
this.areClassesOrInterfacesCompatible(upperBoundClasses, compatibilityUpperBound) == Compatibility.COMPATIBLE)
|
||||
) {
|
||||
return Compatibility.COMPATIBLE
|
||||
}
|
||||
|
||||
val leafClassesOrInterfaces = computeLeafClassesOrInterfaces(upperBoundClasses)
|
||||
this.areClassesOrInterfacesCompatible(leafClassesOrInterfaces, compatibilityUpperBound)?.let { return it }
|
||||
|
||||
// Check if the range formed by upper bounds and lower bounds is empty.
|
||||
if (!lowerBounds.all { lowerBoundType ->
|
||||
val classesSatisfyingLowerBounds =
|
||||
lowerBoundType.toFirClassWithSuperClasses(this)?.thisAndAllSuperClasses ?: emptySet()
|
||||
leafClassesOrInterfaces.all { it in classesSatisfyingLowerBounds }
|
||||
}
|
||||
) {
|
||||
return compatibilityUpperBound
|
||||
}
|
||||
|
||||
if (upperBounds.size < 2) return Compatibility.COMPATIBLE
|
||||
|
||||
// Base types are compatible. Now we check type parameters.
|
||||
|
||||
val typeArgumentMapping = mutableMapOf<FirTypeParameterRef, BoundTypeArguments>().apply {
|
||||
for (type in upperBounds) {
|
||||
collectTypeArgumentMapping(type, this@areCompatible, compatibilityUpperBound)
|
||||
}
|
||||
}
|
||||
var result = Compatibility.COMPATIBLE
|
||||
val typeArgsCompatibility = typeArgumentMapping.values.asSequence()
|
||||
.map { (upper, lower, compatibilityUpperBound) -> areCompatible(upper, lower, compatibilityUpperBound) }
|
||||
for (compatibility in typeArgsCompatibility) {
|
||||
if (compatibility == compatibilityUpperBound) return compatibility
|
||||
if (compatibility > result) {
|
||||
result = compatibility
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the upper bound classes into the class hierarchy and count hows many subclasses are there for each encountered class. Then
|
||||
* output a list of leaf classes or interfaces in the class hierarchy.
|
||||
*/
|
||||
private fun computeLeafClassesOrInterfaces(upperBoundClasses: Set<FirClassWithSuperClasses>): Set<FirClassWithSuperClasses> {
|
||||
val isLeaf = mutableMapOf<FirClassWithSuperClasses, Boolean>()
|
||||
upperBoundClasses.associateWithTo(isLeaf) { true } // implementation of keysToMap actually ends up creating 2 maps so this is better
|
||||
val queue = ArrayDeque(upperBoundClasses)
|
||||
while (queue.isNotEmpty()) {
|
||||
for (superClass in queue.removeFirst().superClasses) {
|
||||
when (isLeaf[superClass]) {
|
||||
true -> isLeaf[superClass] = false
|
||||
false -> {
|
||||
// nothing to be done since this super class has already been handled.
|
||||
}
|
||||
else -> {
|
||||
isLeaf[superClass] = false
|
||||
queue.addLast(superClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return isLeaf.filterValues { it }.keys
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given classes are compatible. In other words, check if it's possible for objects of the given classes to be
|
||||
* considered equal by [Any.equals].
|
||||
*
|
||||
* @return null if this check is inconclusive
|
||||
*/
|
||||
private fun ConeInferenceContext.areClassesOrInterfacesCompatible(
|
||||
classesOrInterfaces: Collection<FirClassWithSuperClasses>,
|
||||
compatibilityUpperBound: Compatibility
|
||||
): Compatibility? {
|
||||
val classes = classesOrInterfaces.filter { !it.isInterface }
|
||||
// Java force single inheritance, so any pair of unrelated classes are incompatible.
|
||||
if (classes.size >= 2) {
|
||||
return if (classes.any { it.getHasPredefinedEqualityContract(this) }) {
|
||||
compatibilityUpperBound
|
||||
} else {
|
||||
Compatibility.SOFT_INCOMPATIBLE
|
||||
}
|
||||
}
|
||||
val finalClass = classes.firstOrNull { it.isFinal } ?: return null
|
||||
// One final class and some other unrelated interface are not compatible
|
||||
if (classesOrInterfaces.size > classes.size) {
|
||||
return if (finalClass.getHasPredefinedEqualityContract(this)) {
|
||||
compatibilityUpperBound
|
||||
} else {
|
||||
Compatibility.SOFT_INCOMPATIBLE
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the upper bounds as [ConeClassLikeType].
|
||||
*/
|
||||
private fun ConeKotlinType?.collectUpperBounds(): Set<ConeClassLikeType> {
|
||||
if (this == null) return emptySet()
|
||||
return when (this) {
|
||||
is ConeClassErrorType -> emptySet() // Ignore error types
|
||||
is ConeLookupTagBasedType -> when (this) {
|
||||
is ConeClassLikeType -> setOf(this)
|
||||
is ConeTypeVariableType -> when (val tag = lookupTag) {
|
||||
is ConeTypeVariableTypeConstructor -> (tag.originalTypeParameter as? ConeTypeParameterLookupTag)?.typeParameterSymbol.collectUpperBounds()
|
||||
else -> throw IllegalStateException("missing branch for ${lookupTag.javaClass.name}")
|
||||
}
|
||||
is ConeTypeParameterType -> lookupTag.typeParameterSymbol.collectUpperBounds()
|
||||
else -> throw IllegalStateException("missing branch for ${javaClass.name}")
|
||||
}
|
||||
is ConeDefinitelyNotNullType -> original.collectUpperBounds()
|
||||
is ConeIntersectionType -> intersectedTypes.flatMap { it.collectUpperBounds() }.toSet()
|
||||
is ConeFlexibleType -> upperBound.collectUpperBounds()
|
||||
is ConeCapturedType, is ConeStubType, is ConeIntegerLiteralType -> throw IllegalStateException("$this should not reach here")
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirTypeParameterSymbol?.collectUpperBounds(): Set<ConeClassLikeType> {
|
||||
if (this == null) return emptySet()
|
||||
return fir.bounds.flatMap { it.coneTypeSafe<ConeKotlinType>().collectUpperBounds() }.toSet()
|
||||
}
|
||||
|
||||
private fun ConeKotlinType?.collectLowerBounds(): Set<ConeClassLikeType> {
|
||||
if (this == null) return emptySet()
|
||||
return when (this) {
|
||||
is ConeClassErrorType -> emptySet() // Ignore error types
|
||||
is ConeLookupTagBasedType -> when (this) {
|
||||
is ConeClassLikeType -> setOf(this)
|
||||
is ConeTypeVariableType -> emptySet()
|
||||
is ConeTypeParameterType -> emptySet()
|
||||
else -> throw IllegalStateException("missing branch for ${javaClass.name}")
|
||||
}
|
||||
is ConeDefinitelyNotNullType -> original.collectLowerBounds()
|
||||
is ConeIntersectionType -> intersectedTypes.flatMap { it.collectLowerBounds() }.toSet()
|
||||
is ConeFlexibleType -> lowerBound.collectLowerBounds()
|
||||
is ConeCapturedType, is ConeStubType, is ConeIntegerLiteralType -> throw IllegalStateException("$this should not reach here")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For each type parameters appeared in the class hierarchy, collect all type arguments that eventually mapped to it. For example,
|
||||
* given type `List<String>`, the returned map contains
|
||||
*
|
||||
* - type parameter of `List` -> upper:[`String`], lower:[]
|
||||
* - type parameter of `Collection` -> upper:[`String`], lower:[]
|
||||
* - type parameter of `Iterable` -> upper:[`String`], lower:[]
|
||||
*
|
||||
* If later `Collection<Int>` is passed to this method with the same receiver map, the receiver map would become:
|
||||
*
|
||||
* - type parameter of `List` -> upper:[`String`], lower:[]
|
||||
* - type parameter of `Collection` -> upper:[`String`, `Int`], lower:[]
|
||||
* - type parameter of `Iterable` -> upper:[`String`, `Int`], lower:[]
|
||||
*/
|
||||
private fun MutableMap<FirTypeParameterRef, BoundTypeArguments>.collectTypeArgumentMapping(
|
||||
coneType: ConeClassLikeType,
|
||||
ctx: ConeInferenceContext,
|
||||
compatibilityUpperBound: Compatibility
|
||||
) {
|
||||
val queue = ArrayDeque<TypeArgumentMapping>()
|
||||
queue.addLast(coneType.toTypeArgumentMapping(ctx) ?: return)
|
||||
while (queue.isNotEmpty()) {
|
||||
val (typeParameterOwner, mapping) = queue.removeFirst()
|
||||
val superTypes = typeParameterOwner.getSuperTypes()
|
||||
for (superType in superTypes) {
|
||||
queue.addLast(superType.toTypeArgumentMapping(ctx, mapping) ?: continue)
|
||||
}
|
||||
for ((firTypeParameterRef, boundTypeArgument) in mapping) {
|
||||
this.collect(ctx, typeParameterOwner, firTypeParameterRef, boundTypeArgument, compatibilityUpperBound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts type arguments in a [ConeClassLikeType] to a [TypeArgumentMapping]. */
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun ConeClassLikeType.toTypeArgumentMapping(
|
||||
ctx: ConeInferenceContext,
|
||||
envMapping: Map<FirTypeParameterRef, BoundTypeArgument> = emptyMap(),
|
||||
): TypeArgumentMapping? {
|
||||
val typeParameterOwner = getClassLikeElement(ctx) ?: return null
|
||||
val mapping = buildMap<FirTypeParameterRef, BoundTypeArgument> {
|
||||
typeArguments.forEachIndexed { index, coneTypeProjection ->
|
||||
val typeParameter: FirTypeParameterRef = typeParameterOwner.getTypeParameter(index) ?: return@forEachIndexed
|
||||
var boundTypeArgument: BoundTypeArgument = when (coneTypeProjection) {
|
||||
// Ignore star since it doesn't provide any constraints.
|
||||
ConeStarProjection -> return@forEachIndexed
|
||||
// Ignore contravariant projection because they induces union types. Hence, whatever type argument should always be
|
||||
// considered compatible.
|
||||
is ConeKotlinTypeProjectionIn -> BoundTypeArgument(coneTypeProjection.type, Variance.IN_VARIANCE)
|
||||
is ConeKotlinTypeProjectionOut -> BoundTypeArgument(coneTypeProjection.type, Variance.OUT_VARIANCE)
|
||||
is ConeKotlinType ->
|
||||
when ((typeParameter as? FirTypeParameter)?.variance) {
|
||||
Variance.IN_VARIANCE -> BoundTypeArgument(coneTypeProjection.type, Variance.IN_VARIANCE)
|
||||
Variance.OUT_VARIANCE -> BoundTypeArgument(coneTypeProjection.type, Variance.OUT_VARIANCE)
|
||||
else -> BoundTypeArgument(coneTypeProjection.type, Variance.INVARIANT)
|
||||
}
|
||||
}
|
||||
val coneKotlinType = boundTypeArgument.type
|
||||
if (coneKotlinType is ConeTypeParameterType) {
|
||||
val envTypeParameter = coneKotlinType.lookupTag.typeParameterSymbol.fir
|
||||
val envTypeArgument = envMapping[envTypeParameter]
|
||||
if (envTypeArgument != null) {
|
||||
boundTypeArgument = envTypeArgument
|
||||
}
|
||||
}
|
||||
put(typeParameter, boundTypeArgument)
|
||||
}
|
||||
}
|
||||
return TypeArgumentMapping(typeParameterOwner, mapping)
|
||||
}
|
||||
|
||||
private fun MutableMap<FirTypeParameterRef, BoundTypeArguments>.collect(
|
||||
ctx: ConeInferenceContext,
|
||||
typeParameterOwner: FirClassLikeDeclaration<*>,
|
||||
parameter: FirTypeParameterRef,
|
||||
boundTypeArgument: BoundTypeArgument,
|
||||
compatibilityUpperBound: Compatibility,
|
||||
) {
|
||||
computeIfAbsent(parameter) {
|
||||
// the semantic of type parameter in Enum and KClass are fixed: values of types with incompatible type parameters are always
|
||||
// incompatible.
|
||||
val compatibilityUpperBoundForTypeArg =
|
||||
if ((ctx.prohibitComparisonOfIncompatibleEnums && typeParameterOwner.symbol.classId == StandardClassIds.Enum) ||
|
||||
(ctx.prohibitComparisonOfIncompatibleClasses && typeParameterOwner.symbol.classId == StandardClassIds.KClass)
|
||||
) {
|
||||
compatibilityUpperBound
|
||||
} else {
|
||||
Compatibility.SOFT_INCOMPATIBLE
|
||||
}
|
||||
BoundTypeArguments(mutableSetOf(), mutableSetOf(), compatibilityUpperBoundForTypeArg)
|
||||
}.let {
|
||||
val type = boundTypeArgument.type
|
||||
if (boundTypeArgument.variance.allowsInPosition) {
|
||||
it.lower += type.collectLowerBounds()
|
||||
}
|
||||
if (boundTypeArgument.variance.allowsOutPosition) {
|
||||
it.upper += type.collectUpperBounds()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirClassLikeDeclaration<*>.getSuperTypes(): List<ConeClassLikeType> {
|
||||
return when (this) {
|
||||
is FirTypeAlias -> listOfNotNull(expandedTypeRef.coneTypeSafe())
|
||||
is FirClass<*> -> superTypeRefs.mapNotNull { it.coneTypeSafe() }
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConeClassLikeType.getClassLikeElement(ctx: ConeInferenceContext): FirClassLikeDeclaration<*>? =
|
||||
ctx.symbolProvider.getSymbolByLookupTag(lookupTag)?.fir
|
||||
|
||||
private fun FirClassLikeDeclaration<*>.getTypeParameter(index: Int): FirTypeParameterRef? {
|
||||
return when (this) {
|
||||
is FirTypeAlias -> typeParameters[index]
|
||||
is FirClass<*> -> typeParameters[index]
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
||||
/** A class declaration and the arguments bound to the declared type parameters. */
|
||||
private data class TypeArgumentMapping(
|
||||
val typeParameterOwner: FirClassLikeDeclaration<*>,
|
||||
val mapping: Map<FirTypeParameterRef, BoundTypeArgument>
|
||||
)
|
||||
|
||||
/** A single bound type argument to a type parameter declared in a class. */
|
||||
private data class BoundTypeArgument(val type: ConeKotlinType, val variance: Variance)
|
||||
|
||||
/** Accumulated type arguments bound to a type parameter declared in a class. */
|
||||
private data class BoundTypeArguments(
|
||||
val upper: MutableSet<ConeClassLikeType>,
|
||||
val lower: MutableSet<ConeClassLikeType>,
|
||||
val compatibilityUpperBound: Compatibility
|
||||
)
|
||||
|
||||
private fun ConeClassLikeType.toFirClassWithSuperClasses(ctx: ConeInferenceContext): FirClassWithSuperClasses? {
|
||||
return lookupTag.toFirClassWithSuperClasses(ctx)
|
||||
}
|
||||
|
||||
private fun ConeClassLikeLookupTag.toFirClassWithSuperClasses(
|
||||
ctx: ConeInferenceContext
|
||||
): FirClassWithSuperClasses? = when (val klass = ctx.symbolProvider.getSymbolByLookupTag(this)?.fir) {
|
||||
is FirTypeAlias -> klass.fullyExpandedClass(ctx.session)?.let { FirClassWithSuperClasses(it, ctx) }
|
||||
is FirClass<*> -> FirClassWithSuperClasses(klass, ctx)
|
||||
else -> null
|
||||
}
|
||||
|
||||
private data class FirClassWithSuperClasses(val firClass: FirClass<*>, val ctx: ConeInferenceContext) {
|
||||
val isInterface: Boolean get() = firClass.isInterface
|
||||
|
||||
val superClasses: Set<FirClassWithSuperClasses> by lazy {
|
||||
firClass.superConeTypes.mapNotNull { it.lookupTag.toFirClassWithSuperClasses(ctx) }.toSet()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
val thisAndAllSuperClasses: Set<FirClassWithSuperClasses> by lazy {
|
||||
val queue = ArrayDeque<FirClassWithSuperClasses>()
|
||||
queue.addLast(this)
|
||||
buildSet {
|
||||
add(this@FirClassWithSuperClasses)
|
||||
while (queue.isNotEmpty()) {
|
||||
val current = queue.removeFirst()
|
||||
val superTypes = current.superClasses
|
||||
superTypes.filterNotTo(queue) { it in this@buildSet }
|
||||
addAll(superTypes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val isFinal: Boolean get() = firClass.isFinal
|
||||
|
||||
/**
|
||||
* The following are considered to have a predefined equality contract:
|
||||
* - enums
|
||||
* - primitives (including unsigned integer types)
|
||||
* - classes
|
||||
* - strings
|
||||
* - objects of data classes
|
||||
* - objects of inline classes
|
||||
* - kotlin.Unit
|
||||
*/
|
||||
fun getHasPredefinedEqualityContract(ctx: ConeInferenceContext): Boolean {
|
||||
return (ctx.prohibitComparisonOfIncompatibleEnums && (firClass.isEnumClass || firClass.classId == StandardClassIds.Enum)) ||
|
||||
firClass.isPrimitiveType() ||
|
||||
(ctx.prohibitComparisonOfIncompatibleClasses && firClass.classId == StandardClassIds.KClass) ||
|
||||
firClass.classId == StandardClassIds.String || firClass.classId == StandardClassIds.Unit ||
|
||||
(firClass is FirRegularClass && (firClass.isData || firClass.isInline))
|
||||
}
|
||||
|
||||
private val FirClass<*>.isFinal: Boolean
|
||||
get() {
|
||||
return when (this) {
|
||||
is FirAnonymousObject -> true
|
||||
is FirRegularClass -> status.modality == Modality.FINAL
|
||||
else -> throw java.lang.IllegalStateException("unknown type of FirClass $this")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val ConeInferenceContext.prohibitComparisonOfIncompatibleEnums: Boolean
|
||||
get() = session.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitComparisonOfIncompatibleEnums)
|
||||
|
||||
private val ConeInferenceContext.prohibitComparisonOfIncompatibleClasses: Boolean
|
||||
get() = session.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitComparisonOfIncompatibleClasses)
|
||||
}
|
||||
@@ -9,12 +9,14 @@ import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.modalityModifier
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.overrideModifier
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.visibilityModifier
|
||||
import org.jetbrains.kotlin.fir.analysis.getChild
|
||||
import org.jetbrains.kotlin.fir.containingClass
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirComponentCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
@@ -34,6 +36,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -88,6 +91,14 @@ fun FirClass<*>.isSupertypeOf(other: FirClass<*>, session: FirSession): Boolean
|
||||
return isSupertypeOf(other, mutableSetOf())
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FirClass associated with this
|
||||
* or null of something goes wrong.
|
||||
*/
|
||||
fun ConeClassLikeType.toClass(session: FirSession): FirClass<*>? {
|
||||
return lookupTag.toSymbol(session).safeAs<FirClassSymbol<*>>()?.fir
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FirRegularClass associated with this
|
||||
* or null of something goes wrong.
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.ConeTypeCompatibilityChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.expressions.FirEqualityOperatorCall
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.ConeTypeCompatibilityChecker.areCompatible
|
||||
|
||||
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
|
||||
// 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.inferenceComponents.ctx
|
||||
val intersectionType = inferenceContext.intersectTypesOrNull(listOf(lType, rType)) as? ConeIntersectionType ?: return
|
||||
|
||||
val compatibility = intersectionType.intersectedTypes.areCompatible(inferenceContext)
|
||||
if (compatibility != ConeTypeCompatibilityChecker.Compatibility.COMPATIBLE) {
|
||||
when (expression.source?.kind) {
|
||||
FirRealSourceElementKind -> {
|
||||
// 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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.isEnumType(
|
||||
context: CheckerContext
|
||||
): Boolean {
|
||||
if (isEnum) return true
|
||||
val firRegularClass = (this as? ConeClassLikeType)?.lookupTag?.toFirRegularClass(context.session) ?: return false
|
||||
return firRegularClass.isEnumClass
|
||||
}
|
||||
}
|
||||
+27
@@ -91,6 +91,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DESERIALIZATION_E
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_UPPER_BOUND
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EMPTY_RANGE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ENUM_AS_SUPERTYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EQUALITY_NOT_APPLICABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EQUALITY_NOT_APPLICABLE_WARNING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ERROR_FROM_JAVA_RESOLUTION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ERROR_IN_CONTRACT_DESCRIPTION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_DECLARATION_WITH_BODY
|
||||
@@ -129,7 +131,10 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_UNDERSCOR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_CANDIDATE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_INFIX_MODIFIER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_LATEINIT_MODIFIER
|
||||
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
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPES_WARNING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZER_TYPE_MISMATCH
|
||||
@@ -521,6 +526,8 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
RENDER_TYPE
|
||||
)
|
||||
map.put(UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, "Extension function type can not be used as an upper bound")
|
||||
map.put(INCOMPATIBLE_TYPES, "Incompatible types: {0} and {1}", RENDER_TYPE, RENDER_TYPE)
|
||||
map.put(INCOMPATIBLE_TYPES_WARNING, "Potentially incompatible types: {0} and {1}", RENDER_TYPE, RENDER_TYPE)
|
||||
|
||||
map.put(
|
||||
BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER,
|
||||
@@ -927,6 +934,26 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
RENDER_TYPE,
|
||||
RENDER_TYPE,
|
||||
)
|
||||
map.put(
|
||||
EQUALITY_NOT_APPLICABLE,
|
||||
"Operator ''{0}'' cannot be applied to ''{1}'' and ''{2}''",
|
||||
TO_STRING,
|
||||
RENDER_TYPE,
|
||||
RENDER_TYPE
|
||||
)
|
||||
map.put(
|
||||
EQUALITY_NOT_APPLICABLE_WARNING,
|
||||
"Comparing with ''{0}'' may not be intended because ''{1}'' and ''{2}'' are incompatible types",
|
||||
TO_STRING,
|
||||
RENDER_TYPE,
|
||||
RENDER_TYPE
|
||||
)
|
||||
map.put(
|
||||
INCOMPATIBLE_ENUM_COMPARISON_ERROR,
|
||||
"Comparison of incompatible enums ''{0}'' and ''{1}'' is always unsuccessful",
|
||||
RENDER_TYPE,
|
||||
RENDER_TYPE
|
||||
)
|
||||
|
||||
// Type alias
|
||||
map.put(TOPLEVEL_TYPEALIASES_ONLY, "Nested and local type aliases are not supported")
|
||||
|
||||
+5
@@ -105,4 +105,9 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
get() = setOf(
|
||||
FirStandaloneQualifierChecker,
|
||||
)
|
||||
|
||||
override val equalityOperatorCallCheckers: Set<FirEqualityOperatorCallChecker>
|
||||
get() = setOf(
|
||||
FirEqualityCompatibilityChecker,
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -735,7 +735,7 @@ class ExpressionsConverter(
|
||||
}
|
||||
return if (whenRefWithSubject != null) {
|
||||
buildEqualityOperatorCall {
|
||||
source = whenCondition.toFirSourceElement()
|
||||
source = whenCondition.toFirSourceElement(FirFakeSourceElementKind.WhenCondition)
|
||||
operation = FirOperation.EQ
|
||||
argumentList = buildBinaryArgumentList(
|
||||
buildWhenSubjectExpression {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,10 +14,10 @@ fun f(): Unit {
|
||||
x == 1
|
||||
x != 1
|
||||
|
||||
A() == 1
|
||||
<!EQUALITY_NOT_APPLICABLE!>A() == 1<!>
|
||||
|
||||
x === "1"
|
||||
x !== "1"
|
||||
<!EQUALITY_NOT_APPLICABLE!>x === "1"<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!>x !== "1"<!>
|
||||
|
||||
x === 1
|
||||
x !== 1
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ val test_dd = d === d || d !== d
|
||||
val test_cc = c === c || c !== c
|
||||
|
||||
// Identity for primitive values of different types (no extra error)
|
||||
val test_zb = z === b || z !== b
|
||||
val test_zb = <!EQUALITY_NOT_APPLICABLE!>z === b<!> || <!EQUALITY_NOT_APPLICABLE!>z !== b<!>
|
||||
|
||||
// Primitive vs nullable
|
||||
val test_znz = z === nz || nz === z || z !== nz || nz !== z
|
||||
|
||||
@@ -5,7 +5,7 @@ import kotlin.reflect.KClass
|
||||
class Foo {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other === null || other::class != this::class) return false
|
||||
if (other === null || <!EQUALITY_NOT_APPLICABLE!>other::class != this::class<!>) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
//KT-1185 Support full enumeration check for 'when'
|
||||
|
||||
package kt1185
|
||||
|
||||
enum class Direction {
|
||||
NORTH,
|
||||
SOUTH,
|
||||
WEST,
|
||||
EAST
|
||||
}
|
||||
|
||||
class A {
|
||||
companion object {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
enum class Color(val rgb : Int) {
|
||||
RED(0xFF0000),
|
||||
GREEN(0x00FF00),
|
||||
BLUE(0x0000FF)
|
||||
}
|
||||
|
||||
fun foo(d: Direction) = when(d) { //no 'else' should be requested
|
||||
Direction.NORTH -> 1
|
||||
Direction.SOUTH -> 2
|
||||
A -> 1
|
||||
Direction.WEST -> 3
|
||||
Direction.EAST -> 4
|
||||
}
|
||||
|
||||
fun foo1(d: Direction) = <!NO_ELSE_IN_WHEN!>when<!>(d) {
|
||||
Direction.NORTH -> 1
|
||||
Direction.SOUTH -> 2
|
||||
Direction.WEST -> 3
|
||||
}
|
||||
|
||||
fun bar(c: Color) = when (c) {
|
||||
Color.RED -> 1
|
||||
Color.GREEN -> 2
|
||||
Color.BLUE -> 3
|
||||
}
|
||||
|
||||
fun bar1(c: Color) = <!NO_ELSE_IN_WHEN!>when<!> (c) {
|
||||
Color.RED -> 1
|
||||
Color.GREEN -> 2
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
//KT-1185 Support full enumeration check for 'when'
|
||||
|
||||
package kt1185
|
||||
@@ -44,4 +45,4 @@ fun bar(c: Color) = when (c) {
|
||||
fun bar1(c: Color) = <!NO_ELSE_IN_WHEN!>when<!> (c) {
|
||||
Color.RED -> 1
|
||||
Color.GREEN -> 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@ public enum JavaEnumB {}
|
||||
enum class KotlinEnumA
|
||||
enum class KotlinEnumB
|
||||
|
||||
fun jj(a: JavaEnumA, b: JavaEnumB) = a == b
|
||||
fun jk(a: JavaEnumA, b: KotlinEnumB) = a == b
|
||||
fun kk(a: KotlinEnumA, b: KotlinEnumB) = a == b
|
||||
fun jj(a: JavaEnumA, b: JavaEnumB) = <!EQUALITY_NOT_APPLICABLE_WARNING!>a == b<!>
|
||||
fun jk(a: JavaEnumA, b: KotlinEnumB) = <!EQUALITY_NOT_APPLICABLE_WARNING!>a == b<!>
|
||||
fun kk(a: KotlinEnumA, b: KotlinEnumB) = <!EQUALITY_NOT_APPLICABLE_WARNING!>a == b<!>
|
||||
|
||||
@@ -32,8 +32,8 @@ fun useEn2(x: En2) = x
|
||||
fun bar(x: Any) {
|
||||
if (x is En && x is En2) {
|
||||
when (x) {
|
||||
En.A -> useEn(x)
|
||||
En2.D -> useEn2(x)
|
||||
<!INCOMPATIBLE_TYPES!>En.A<!> -> useEn(x)
|
||||
<!INCOMPATIBLE_TYPES!>En2.D<!> -> useEn2(x)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-15
@@ -7,15 +7,15 @@ interface I {
|
||||
enum class E1 : I {
|
||||
A {
|
||||
override fun foo() {
|
||||
this == E2.A
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>this == E2.A<!>
|
||||
|
||||
val q = this
|
||||
when (q) {
|
||||
this -> {}
|
||||
E1.A -> {}
|
||||
E1.B -> {}
|
||||
E2.A -> {}
|
||||
E2.B -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>E2.A<!> -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>E2.B<!> -> {}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -41,13 +41,13 @@ enum class E2 : I {
|
||||
}
|
||||
|
||||
fun foo1(e1: E1, e2: E2) {
|
||||
e1 == e2
|
||||
e1 != e2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == e2<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 != e2<!>
|
||||
|
||||
e1 == E2.A
|
||||
E1.B == e2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == E2.A<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E1.B == e2<!>
|
||||
|
||||
E1.A == E2.B
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E1.A == E2.B<!>
|
||||
|
||||
e1 == E1.A
|
||||
E1.A == e1
|
||||
@@ -58,27 +58,27 @@ fun foo1(e1: E1, e2: E2) {
|
||||
fun foo2(e1: E1, e2: E2) {
|
||||
when (e1) {
|
||||
E1.A -> {}
|
||||
E2.A -> {}
|
||||
E2.B -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>E2.A<!> -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>E2.B<!> -> {}
|
||||
e1 -> {}
|
||||
e2 -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>e2<!> -> {}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun foo3(e1: Enum<E1>, e2: Enum<E2>, e: Enum<*>) {
|
||||
e1 == e
|
||||
e1 == e2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == e2<!>
|
||||
|
||||
e1 == E1.A
|
||||
e1 == E2.A
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == E2.A<!>
|
||||
|
||||
when (e1) {
|
||||
e1 -> {}
|
||||
e2 -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>e2<!> -> {}
|
||||
e -> {}
|
||||
E1.A -> {}
|
||||
E2.A -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>E2.A<!> -> {}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@ enum class E2 {
|
||||
}
|
||||
|
||||
fun foo1(e1: E1, e2: E2) {
|
||||
e1 == e2
|
||||
e1 != e2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == e2<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 != e2<!>
|
||||
|
||||
e1 == E2.A
|
||||
E1.B == e2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == E2.A<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E1.B == e2<!>
|
||||
|
||||
E1.A == E2.B
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E1.A == E2.B<!>
|
||||
|
||||
e1 == E1.A
|
||||
E1.A == e1
|
||||
@@ -26,27 +26,27 @@ fun foo1(e1: E1, e2: E2) {
|
||||
fun foo2(e1: E1, e2: E2) {
|
||||
when (e1) {
|
||||
E1.A -> {}
|
||||
E2.A -> {}
|
||||
E2.B -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>E2.A<!> -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>E2.B<!> -> {}
|
||||
e1 -> {}
|
||||
e2 -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>e2<!> -> {}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun foo3(e1: Enum<E1>, e2: Enum<E2>, e: Enum<*>) {
|
||||
e1 == e
|
||||
e1 == e2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == e2<!>
|
||||
|
||||
e1 == E1.A
|
||||
e1 == E2.A
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == E2.A<!>
|
||||
|
||||
when (e1) {
|
||||
e1 -> {}
|
||||
e2 -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>e2<!> -> {}
|
||||
e -> {}
|
||||
E1.A -> {}
|
||||
E2.A -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>E2.A<!> -> {}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
@@ -63,15 +63,15 @@ interface MyInterface
|
||||
open class MyOpenClass
|
||||
|
||||
fun foo4(e1: E1, i: MyInterface, c: MyOpenClass) {
|
||||
e1 == i
|
||||
i == e1
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == i<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>i == e1<!>
|
||||
|
||||
e1 == c
|
||||
c == e1
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == c<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>c == e1<!>
|
||||
|
||||
when (e1) {
|
||||
i -> {}
|
||||
c -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>i<!> -> {}
|
||||
<!INCOMPATIBLE_TYPES_WARNING!>c<!> -> {}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -90,10 +90,10 @@ fun foo6(e1: E1?, e2: E2) {
|
||||
e1 == null
|
||||
null == e1
|
||||
|
||||
e1 == E2.A
|
||||
E2.A == e1
|
||||
e1 == e2
|
||||
e2 == e1
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == E2.A<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E2.A == e1<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == e2<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e2 == e1<!>
|
||||
|
||||
e2 == null
|
||||
null == e2
|
||||
@@ -117,16 +117,16 @@ fun <T> foo8(e1: E1?, e2: E2, t: T) {
|
||||
}
|
||||
|
||||
fun <T, K> foo9(e1: E1?, e2: E2, t: T, k: K) where T : MyInterface, T : MyOpenClass, K : MyInterface {
|
||||
e1 == t
|
||||
t == e1
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == t<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>t == e1<!>
|
||||
|
||||
e2 == t
|
||||
t == e2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e2 == t<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>t == e2<!>
|
||||
|
||||
E1.A == t
|
||||
t == E1.A
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E1.A == t<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>t == E1.A<!>
|
||||
|
||||
E3.X == t
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E3.X == t<!>
|
||||
|
||||
E3.X == k
|
||||
k == E3.X
|
||||
@@ -137,9 +137,9 @@ interface Inv<T>
|
||||
enum class E4 : Inv<Int> { A }
|
||||
|
||||
fun foo10(e4: E4, invString: Inv<String>) {
|
||||
e4 == invString
|
||||
invString == e4
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e4 == invString<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>invString == e4<!>
|
||||
|
||||
E4.A == invString
|
||||
invString == E4.A
|
||||
}
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E4.A == invString<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>invString == E4.A<!>
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ enum class E2 {
|
||||
}
|
||||
|
||||
fun foo1(e1: E1, e2: E2) {
|
||||
e1 == e2
|
||||
e1 != e2
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e1 == e2<!>
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e1 != e2<!>
|
||||
|
||||
e1 == E2.A
|
||||
E1.B == e2
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e1 == E2.A<!>
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>E1.B == e2<!>
|
||||
|
||||
E1.A == E2.B
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>E1.A == E2.B<!>
|
||||
|
||||
e1 == E1.A
|
||||
E1.A == e1
|
||||
@@ -26,27 +26,27 @@ fun foo1(e1: E1, e2: E2) {
|
||||
fun foo2(e1: E1, e2: E2) {
|
||||
when (e1) {
|
||||
E1.A -> {}
|
||||
E2.A -> {}
|
||||
E2.B -> {}
|
||||
<!INCOMPATIBLE_TYPES!>E2.A<!> -> {}
|
||||
<!INCOMPATIBLE_TYPES!>E2.B<!> -> {}
|
||||
e1 -> {}
|
||||
e2 -> {}
|
||||
<!INCOMPATIBLE_TYPES!>e2<!> -> {}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun foo3(e1: Enum<E1>, e2: Enum<E2>, e: Enum<*>) {
|
||||
e1 == e
|
||||
e1 == e2
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e1 == e2<!>
|
||||
|
||||
e1 == E1.A
|
||||
e1 == E2.A
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e1 == E2.A<!>
|
||||
|
||||
when (e1) {
|
||||
e1 -> {}
|
||||
e2 -> {}
|
||||
<!INCOMPATIBLE_TYPES!>e2<!> -> {}
|
||||
e -> {}
|
||||
E1.A -> {}
|
||||
E2.A -> {}
|
||||
<!INCOMPATIBLE_TYPES!>E2.A<!> -> {}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
@@ -63,15 +63,15 @@ interface MyInterface
|
||||
open class MyOpenClass
|
||||
|
||||
fun foo4(e1: E1, i: MyInterface, c: MyOpenClass) {
|
||||
e1 == i
|
||||
i == e1
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e1 == i<!>
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>i == e1<!>
|
||||
|
||||
e1 == c
|
||||
c == e1
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e1 == c<!>
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>c == e1<!>
|
||||
|
||||
when (e1) {
|
||||
i -> {}
|
||||
c -> {}
|
||||
<!INCOMPATIBLE_TYPES!>i<!> -> {}
|
||||
<!INCOMPATIBLE_TYPES!>c<!> -> {}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -90,10 +90,10 @@ fun foo6(e1: E1?, e2: E2) {
|
||||
e1 == null
|
||||
null == e1
|
||||
|
||||
e1 == E2.A
|
||||
E2.A == e1
|
||||
e1 == e2
|
||||
e2 == e1
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e1 == E2.A<!>
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>E2.A == e1<!>
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e1 == e2<!>
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e2 == e1<!>
|
||||
|
||||
e2 == null
|
||||
null == e2
|
||||
@@ -117,16 +117,16 @@ fun <T> foo8(e1: E1?, e2: E2, t: T) {
|
||||
}
|
||||
|
||||
fun <T, K> foo9(e1: E1?, e2: E2, t: T, k: K) where T : MyInterface, T : MyOpenClass, K : MyInterface {
|
||||
e1 == t
|
||||
t == e1
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e1 == t<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>t == e1<!>
|
||||
|
||||
e2 == t
|
||||
t == e2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e2 == t<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>t == e2<!>
|
||||
|
||||
E1.A == t
|
||||
t == E1.A
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E1.A == t<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>t == E1.A<!>
|
||||
|
||||
E3.X == t
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E3.X == t<!>
|
||||
|
||||
E3.X == k
|
||||
k == E3.X
|
||||
@@ -137,9 +137,9 @@ interface Inv<T>
|
||||
enum class E4 : Inv<Int> { A }
|
||||
|
||||
fun foo10(e4: E4, invString: Inv<String>) {
|
||||
e4 == invString
|
||||
invString == e4
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>e4 == invString<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>invString == e4<!>
|
||||
|
||||
E4.A == invString
|
||||
invString == E4.A
|
||||
}
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>E4.A == invString<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>invString == E4.A<!>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
class A<T>
|
||||
class AIn<in T>
|
||||
class AOut<out T>
|
||||
class A2
|
||||
|
||||
open class B<T>
|
||||
open class BIn<in T>
|
||||
open class BOut<out T>
|
||||
|
||||
open class C
|
||||
open class D
|
||||
|
||||
enum class E
|
||||
interface I
|
||||
|
||||
interface T<T>
|
||||
|
||||
open class TSub1 : T<String>
|
||||
open class TSub2 : T<Int>
|
||||
|
||||
fun foo(
|
||||
string: String, int: Int,
|
||||
strings: List<String>, ints: List<Int>,
|
||||
|
||||
aString: A<String>, aInt: A<Int>,
|
||||
aOutString: A<out String>, aOutInt: A<out Int>,
|
||||
aOutString2: AOut<String>, aOutInt2: AOut<Int>,
|
||||
aInString: A<in String>, aInInt: A<in Int>,
|
||||
aInString2: AIn<String>, aInInt2: AIn<Int>,
|
||||
|
||||
bString: B<String>, bInt: B<Int>,
|
||||
bOutString: B<out String>, bOutInt: B<out Int>,
|
||||
bOutString2: BOut<String>, bOutInt2: BOut<Int>,
|
||||
bInString: B<in String>, bInInt: B<in Int>,
|
||||
bInString2: BIn<String>, bInInt2: BIn<Int>,
|
||||
|
||||
a2: A2,
|
||||
|
||||
e: E,
|
||||
i: I,
|
||||
|
||||
ac: A<C>, ad: A<D>,
|
||||
|
||||
tSub1: TSub1,
|
||||
tSub2: TSub2,
|
||||
|
||||
aListInt: A<List<Int>>,
|
||||
aSetInt: A<Set<Int>>,
|
||||
aListString: A<List<String>>,
|
||||
) {
|
||||
"a" == "b"
|
||||
1 == 2
|
||||
<!EQUALITY_NOT_APPLICABLE!>"" == 2<!>
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE!>string == int<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>strings == ints<!>
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aString == aInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aOutString == aOutInt<!>
|
||||
aInString == aInInt
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aOutString == aInInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aInString == aOutInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aOutString == aInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aInString == aInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aOutString2 == aOutInt2<!>
|
||||
aInString2 == aInInt2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aOutString2 == aInInt2<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aInString2 == aOutInt2<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aString == a2<!>
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bString == bInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bOutString == bOutInt<!>
|
||||
bInString == bInInt
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bOutString == bInInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bInString == bOutInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bOutString == bInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bInString == bInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bOutString2 == bOutInt2<!>
|
||||
bInString2 == bInInt2
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bOutString2 == bInInt2<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bInString2 == bOutInt2<!>
|
||||
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e == i<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!>"" == i<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>ac == ad<!>
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>tSub1 == tSub2<!>
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aString == bString<!>
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aListInt == aSetInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aSetInt == aListString<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aListString == aListInt<!>
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>aString == aListString<!>
|
||||
<!EQUALITY_NOT_APPLICABLE_WARNING!>bString == aListString<!>
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
class A<T>
|
||||
class AIn<in T>
|
||||
class AOut<out T>
|
||||
class A2
|
||||
|
||||
open class B<T>
|
||||
open class BIn<in T>
|
||||
open class BOut<out T>
|
||||
|
||||
open class C
|
||||
open class D
|
||||
|
||||
enum class E
|
||||
interface I
|
||||
|
||||
interface T<T>
|
||||
|
||||
open class TSub1 : T<String>
|
||||
open class TSub2 : T<Int>
|
||||
|
||||
fun foo(
|
||||
string: String, int: Int,
|
||||
strings: List<String>, ints: List<Int>,
|
||||
|
||||
aString: A<String>, aInt: A<Int>,
|
||||
aOutString: A<out String>, aOutInt: A<out Int>,
|
||||
aOutString2: AOut<String>, aOutInt2: AOut<Int>,
|
||||
aInString: A<in String>, aInInt: A<in Int>,
|
||||
aInString2: AIn<String>, aInInt2: AIn<Int>,
|
||||
|
||||
bString: B<String>, bInt: B<Int>,
|
||||
bOutString: B<out String>, bOutInt: B<out Int>,
|
||||
bOutString2: BOut<String>, bOutInt2: BOut<Int>,
|
||||
bInString: B<in String>, bInInt: B<in Int>,
|
||||
bInString2: BIn<String>, bInInt2: BIn<Int>,
|
||||
|
||||
a2: A2,
|
||||
|
||||
e: E,
|
||||
i: I,
|
||||
|
||||
ac: A<C>, ad: A<D>,
|
||||
|
||||
tSub1: TSub1,
|
||||
tSub2: TSub2,
|
||||
|
||||
aListInt: A<List<Int>>,
|
||||
aSetInt: A<Set<Int>>,
|
||||
aListString: A<List<String>>,
|
||||
) {
|
||||
"a" == "b"
|
||||
1 == 2
|
||||
<!EQUALITY_NOT_APPLICABLE!>"" == 2<!>
|
||||
|
||||
<!EQUALITY_NOT_APPLICABLE!>string == int<!>
|
||||
strings == ints
|
||||
|
||||
aString == aInt
|
||||
<!EQUALITY_NOT_APPLICABLE!>aOutString == aOutInt<!>
|
||||
aInString == aInInt
|
||||
<!EQUALITY_NOT_APPLICABLE!>aOutString == aInInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!>aInString == aOutInt<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!>aOutString == aInt<!>
|
||||
aInString == aInt
|
||||
<!EQUALITY_NOT_APPLICABLE!>aOutString2 == aOutInt2<!>
|
||||
aInString2 == aInInt2
|
||||
<!EQUALITY_NOT_APPLICABLE!>aOutString2 == aInInt2<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!>aInString2 == aOutInt2<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!>aString == a2<!>
|
||||
|
||||
bString == bInt
|
||||
bOutString == bOutInt
|
||||
bInString == bInInt
|
||||
bOutString == bInInt
|
||||
bInString == bOutInt
|
||||
bOutString == bInt
|
||||
bInString == bInt
|
||||
bOutString2 == bOutInt2
|
||||
bInString2 == bInInt2
|
||||
bOutString2 == bInInt2
|
||||
bInString2 == bOutInt2
|
||||
|
||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>e == i<!>
|
||||
<!EQUALITY_NOT_APPLICABLE!>"" == i<!>
|
||||
ac == ad
|
||||
|
||||
tSub1 == tSub2
|
||||
|
||||
aString == bString
|
||||
|
||||
aListInt == aSetInt
|
||||
aSetInt == aListString
|
||||
aListString == aListInt
|
||||
|
||||
aString == aListString
|
||||
bString == aListString
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ string: kotlin.String, /*1*/ int: kotlin.Int, /*2*/ strings: kotlin.collections.List<kotlin.String>, /*3*/ ints: kotlin.collections.List<kotlin.Int>, /*4*/ aString: A<kotlin.String>, /*5*/ aInt: A<kotlin.Int>, /*6*/ aOutString: A<out kotlin.String>, /*7*/ aOutInt: A<out kotlin.Int>, /*8*/ aOutString2: AOut<kotlin.String>, /*9*/ aOutInt2: AOut<kotlin.Int>, /*10*/ aInString: A<in kotlin.String>, /*11*/ aInInt: A<in kotlin.Int>, /*12*/ aInString2: AIn<kotlin.String>, /*13*/ aInInt2: AIn<kotlin.Int>, /*14*/ bString: B<kotlin.String>, /*15*/ bInt: B<kotlin.Int>, /*16*/ bOutString: B<out kotlin.String>, /*17*/ bOutInt: B<out kotlin.Int>, /*18*/ bOutString2: BOut<kotlin.String>, /*19*/ bOutInt2: BOut<kotlin.Int>, /*20*/ bInString: B<in kotlin.String>, /*21*/ bInInt: B<in kotlin.Int>, /*22*/ bInString2: BIn<kotlin.String>, /*23*/ bInInt2: BIn<kotlin.Int>, /*24*/ a2: A2, /*25*/ e: E, /*26*/ i: I, /*27*/ ac: A<C>, /*28*/ ad: A<D>, /*29*/ tSub1: TSub1, /*30*/ tSub2: TSub2, /*31*/ aListInt: A<kotlin.collections.List<kotlin.Int>>, /*32*/ aSetInt: A<kotlin.collections.Set<kotlin.Int>>, /*33*/ aListString: A<kotlin.collections.List<kotlin.String>>, /*34*/ mutableListAny: kotlin.collections.MutableList<kotlin.Any>, /*35*/ listString: kotlin.collections.List<kotlin.String>): kotlin.Unit
|
||||
|
||||
public final class A</*0*/ T> {
|
||||
public constructor A</*0*/ T>()
|
||||
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 class A2 {
|
||||
public constructor A2()
|
||||
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 class AIn</*0*/ in T> {
|
||||
public constructor AIn</*0*/ in T>()
|
||||
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 class AOut</*0*/ out T> {
|
||||
public constructor AOut</*0*/ out T>()
|
||||
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 open class B</*0*/ T> {
|
||||
public constructor B</*0*/ T>()
|
||||
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 open class BIn</*0*/ in T> {
|
||||
public constructor BIn</*0*/ in T>()
|
||||
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 open class BOut</*0*/ out T> {
|
||||
public constructor BOut</*0*/ out T>()
|
||||
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 open class C {
|
||||
public constructor C()
|
||||
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 open class D {
|
||||
public constructor D()
|
||||
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 enum class E : kotlin.Enum<E> {
|
||||
private constructor E()
|
||||
public final override /*1*/ /*fake_override*/ val name: kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
|
||||
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
|
||||
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
|
||||
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<E!>!
|
||||
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
// Static members
|
||||
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E
|
||||
public final /*synthesized*/ fun values(): kotlin.Array<E>
|
||||
}
|
||||
|
||||
public interface I {
|
||||
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 interface T</*0*/ T> {
|
||||
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 open class TSub1 : T<kotlin.String> {
|
||||
public constructor TSub1()
|
||||
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 open class TSub2 : T<kotlin.Int> {
|
||||
public constructor TSub2()
|
||||
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
|
||||
}
|
||||
Vendored
+3
-3
@@ -7,11 +7,11 @@ inline class Bar(val y: String)
|
||||
fun test(f1: Foo, f2: Foo, b1: Bar, fn1: Foo?, fn2: Foo?) {
|
||||
val a1 = f1 === f2 || f1 !== f2
|
||||
val a2 = f1 === f1
|
||||
val a3 = f1 === b1 || f1 !== b1
|
||||
val a3 = <!EQUALITY_NOT_APPLICABLE!>f1 === b1<!> || <!EQUALITY_NOT_APPLICABLE!>f1 !== b1<!>
|
||||
|
||||
val c1 = fn1 === fn2 || fn1 !== fn2
|
||||
val c2 = f1 === fn1 || f1 !== fn1
|
||||
val c3 = b1 === fn1 || b1 !== fn1
|
||||
val c3 = <!EQUALITY_NOT_APPLICABLE!>b1 === fn1<!> || <!EQUALITY_NOT_APPLICABLE!>b1 !== fn1<!>
|
||||
|
||||
val any = Any()
|
||||
|
||||
@@ -19,4 +19,4 @@ fun test(f1: Foo, f2: Foo, b1: Bar, fn1: Foo?, fn2: Foo?) {
|
||||
val d2 = f1 === any || f1 !== any
|
||||
val d3 = any === fn1 || any !== fn1
|
||||
val d4 = fn1 === any || fn1 !== any
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import checkSubtype
|
||||
fun main(args : Array<String>) {
|
||||
val x = checkSubtype<Any>(args[0])
|
||||
if(x is java.lang.CharSequence) {
|
||||
if ("a" == x) x.<!UNRESOLVED_REFERENCE!>length<!> else x.length() // OK
|
||||
if ("a" == x || "b" == x) x.<!UNRESOLVED_REFERENCE!>length<!> else x.length() // <– THEN ERROR
|
||||
if ("a" == x && "a" == x) x.<!UNRESOLVED_REFERENCE!>length<!> else x.length() // <– ELSE ERROR
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>"a" == x<!>) x.<!UNRESOLVED_REFERENCE!>length<!> else x.length() // OK
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>"a" == x<!> || <!EQUALITY_NOT_APPLICABLE!>"b" == x<!>) x.<!UNRESOLVED_REFERENCE!>length<!> else x.length() // <– THEN ERROR
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>"a" == x<!> && <!EQUALITY_NOT_APPLICABLE!>"a" == x<!>) x.<!UNRESOLVED_REFERENCE!>length<!> else x.length() // <– ELSE ERROR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ sealed class Tree {
|
||||
|
||||
fun maxIsClass(): Int = <!NO_ELSE_IN_WHEN!>when<!>(this) {
|
||||
Empty -> -1
|
||||
<!NO_COMPANION_OBJECT!>Leaf<!> -> 0
|
||||
<!INCOMPATIBLE_TYPES, NO_COMPANION_OBJECT!>Leaf<!> -> 0
|
||||
is Node -> this.left.max()
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ fun test(
|
||||
val ui = ui1 === ui2 || ui1 !== ui2
|
||||
val ul = ul1 === ul2 || ul1 !== ul2
|
||||
|
||||
val u = ub1 === ul1
|
||||
val u = <!EQUALITY_NOT_APPLICABLE!>ub1 === ul1<!>
|
||||
|
||||
val a1 = 1u === 2u || 1u !== 2u
|
||||
val a2 = 0xFFFF_FFFF_FFFF_FFFFu === 0xFFFF_FFFF_FFFF_FFFFu
|
||||
@@ -20,4 +20,4 @@ fun test(
|
||||
val bu2 = 1u
|
||||
|
||||
val c1 = bu1 === bu2 || bu1 !== bu2
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,11 +14,11 @@ value class Bar(val y: String)
|
||||
fun test(f1: Foo, f2: Foo, b1: Bar, fn1: Foo?, fn2: Foo?) {
|
||||
val a1 = f1 === f2 || f1 !== f2
|
||||
val a2 = f1 === f1
|
||||
val a3 = f1 === b1 || f1 !== b1
|
||||
val a3 = <!EQUALITY_NOT_APPLICABLE_WARNING!>f1 === b1<!> || <!EQUALITY_NOT_APPLICABLE_WARNING!>f1 !== b1<!>
|
||||
|
||||
val c1 = fn1 === fn2 || fn1 !== fn2
|
||||
val c2 = f1 === fn1 || f1 !== fn1
|
||||
val c3 = b1 === fn1 || b1 !== fn1
|
||||
val c3 = <!EQUALITY_NOT_APPLICABLE_WARNING!>b1 === fn1<!> || <!EQUALITY_NOT_APPLICABLE_WARNING!>b1 !== fn1<!>
|
||||
|
||||
val any = Any()
|
||||
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ fun foo() : Int {
|
||||
<!USELESS_IS_CHECK!>!is Int<!> -> 1
|
||||
<!USELESS_IS_CHECK!>is Any?<!> -> 1
|
||||
<!USELESS_IS_CHECK!>is Any<!> -> 1
|
||||
s -> 1
|
||||
<!INCOMPATIBLE_TYPES!>s<!> -> 1
|
||||
1 -> 1
|
||||
1 <!OVERLOAD_RESOLUTION_AMBIGUITY!>+<!> <!UNRESOLVED_REFERENCE!>a<!> -> 1
|
||||
in 1..<!UNRESOLVED_REFERENCE!>a<!> -> 1
|
||||
@@ -41,8 +41,8 @@ fun test() {
|
||||
val s = "";
|
||||
|
||||
when (x) {
|
||||
s -> 1
|
||||
"" -> 1
|
||||
<!INCOMPATIBLE_TYPES!>s<!> -> 1
|
||||
<!INCOMPATIBLE_TYPES!>""<!> -> 1
|
||||
x -> 1
|
||||
1 -> 1
|
||||
}
|
||||
|
||||
Generated
+6
@@ -9077,6 +9077,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/enum/starImportNestedClassAndEntries.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeCompatibility.kt")
|
||||
public void testTypeCompatibility() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/enum/typeCompatibility.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeParametersInEnum.kt")
|
||||
public void testTypeParametersInEnum() throws Exception {
|
||||
|
||||
Vendored
+2
-2
@@ -3,8 +3,8 @@
|
||||
// TESTCASE NUMBER: 1
|
||||
fun case_1(value_1: Int, value_2: TypesProvider): String {
|
||||
when (value_1) {
|
||||
-1000L..100 -> return ""
|
||||
value_2.getInt()..getLong() -> return ""
|
||||
<!INCOMPATIBLE_TYPES!>-1000L..100<!> -> return ""
|
||||
<!INCOMPATIBLE_TYPES!>value_2.getInt()..getLong()<!> -> return ""
|
||||
}
|
||||
|
||||
return ""
|
||||
|
||||
@@ -55,7 +55,7 @@ fun case_4(x: Char?) {
|
||||
fun case_5() {
|
||||
val x: Unit? = null
|
||||
|
||||
if (x !== <!USELESS_IS_CHECK!>null is Boolean?<!>) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Unit?")!>x<!>
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>x !== <!USELESS_IS_CHECK!>null is Boolean?<!>) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Unit?")!>x<!>
|
||||
if (x !== null == null) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Unit?")!>x<!><!UNSAFE_CALL!>.<!>equals(null)
|
||||
if (x !== null == null) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Unit?")!>x<!>.propT
|
||||
if (x !== null == null) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Unit?")!>x<!><!UNSAFE_CALL!>.<!>propAny
|
||||
@@ -87,7 +87,7 @@ fun case_6(x: EmptyClass?) {
|
||||
|
||||
// TESTCASE NUMBER: 7
|
||||
fun case_7() {
|
||||
if (nullableNumberProperty != null || <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number?")!>nullableNumberProperty<!> != null is Boolean) {
|
||||
if (nullableNumberProperty != null || <!EQUALITY_NOT_APPLICABLE!><!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number?")!>nullableNumberProperty<!> != null is Boolean<!>) {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number?")!>nullableNumberProperty<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number?")!>nullableNumberProperty<!><!UNSAFE_CALL!>.<!>equals(null)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Number?")!>nullableNumberProperty<!>.propT
|
||||
@@ -141,12 +141,12 @@ fun case_10() {
|
||||
fun case_11(x: TypealiasNullableStringIndirect?, y: TypealiasNullableStringIndirect) {
|
||||
val t: TypealiasNullableStringIndirect = null
|
||||
|
||||
if (x == null is Boolean) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>x == null is Boolean<!>) {
|
||||
|
||||
} else {
|
||||
if (y != null is Boolean == true) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>y != null is Boolean<!> == true) {
|
||||
if (<!USELESS_IS_CHECK!>(nullableStringProperty == null) !is Boolean<!>) {
|
||||
if (t != null is Boolean) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>t != null is Boolean<!>) {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasNullableStringIndirect?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasNullableStringIndirect?")!>x<!>.equals(null)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasNullableStringIndirect?")!>x<!>.propT
|
||||
@@ -236,7 +236,7 @@ fun case_14() {
|
||||
|
||||
// TESTCASE NUMBER: 15
|
||||
fun case_15(x: EmptyObject) {
|
||||
val t = if (x === <!USELESS_IS_CHECK!><!USELESS_IS_CHECK!>null is Boolean is Boolean<!> is Boolean<!>) "" else {
|
||||
val t = if (<!EQUALITY_NOT_APPLICABLE!>x === <!USELESS_IS_CHECK!><!USELESS_IS_CHECK!>null is Boolean is Boolean<!> is Boolean<!>) "" else {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("EmptyObject")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("EmptyObject")!>x<!>.equals(null)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("EmptyObject")!>x<!>.propT
|
||||
@@ -254,7 +254,7 @@ fun case_15(x: EmptyObject) {
|
||||
fun case_16() {
|
||||
val x: TypealiasNullableNothing = null
|
||||
|
||||
if (x != <!USELESS_IS_CHECK!><!USELESS_IS_CHECK!><!USELESS_IS_CHECK!><!USELESS_IS_CHECK!>null !is Boolean !is Boolean<!> !is Boolean<!> !is Boolean<!> !is Boolean<!>) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>x != <!USELESS_IS_CHECK!><!USELESS_IS_CHECK!><!USELESS_IS_CHECK!><!USELESS_IS_CHECK!>null !is Boolean !is Boolean<!> !is Boolean<!> !is Boolean<!> !is Boolean<!>) {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasNullableNothing")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasNullableNothing")!>x<!><!UNSAFE_CALL!>.<!>java
|
||||
}
|
||||
@@ -302,7 +302,7 @@ fun case_19(b: Boolean) {
|
||||
}
|
||||
} else null
|
||||
|
||||
if (a != null !is Boolean && a<!UNSAFE_CALL!>.<!>B19 != null is Boolean && a<!UNSAFE_CALL!>.<!>B19<!UNSAFE_CALL!>.<!>C19 != null is Boolean && a<!UNSAFE_CALL!>.<!>B19<!UNSAFE_CALL!>.<!>C19<!UNSAFE_CALL!>.<!>D19 != null == null && a<!UNSAFE_CALL!>.<!>B19<!UNSAFE_CALL!>.<!>C19<!UNSAFE_CALL!>.<!>D19<!UNSAFE_CALL!>.<!>x != null !== null) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>a != null !is Boolean<!> && <!EQUALITY_NOT_APPLICABLE!>a<!UNSAFE_CALL!>.<!>B19 != null is Boolean<!> && <!EQUALITY_NOT_APPLICABLE!>a<!UNSAFE_CALL!>.<!>B19<!UNSAFE_CALL!>.<!>C19 != null is Boolean<!> && a<!UNSAFE_CALL!>.<!>B19<!UNSAFE_CALL!>.<!>C19<!UNSAFE_CALL!>.<!>D19 != null == null && a<!UNSAFE_CALL!>.<!>B19<!UNSAFE_CALL!>.<!>C19<!UNSAFE_CALL!>.<!>D19<!UNSAFE_CALL!>.<!>x != null !== null) {
|
||||
a<!UNSAFE_CALL!>.<!>B19<!UNSAFE_CALL!>.<!>C19<!UNSAFE_CALL!>.<!>D19<!UNSAFE_CALL!>.<!>x
|
||||
a<!UNSAFE_CALL!>.<!>B19<!UNSAFE_CALL!>.<!>C19<!UNSAFE_CALL!>.<!>D19<!UNSAFE_CALL!>.<!>x<!UNSAFE_CALL!>.<!>equals(null)
|
||||
a<!UNSAFE_CALL!>.<!>B19<!UNSAFE_CALL!>.<!>C19<!UNSAFE_CALL!>.<!>D19<!UNSAFE_CALL!>.<!>x.propT
|
||||
@@ -328,7 +328,7 @@ fun case_20(b: Boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
if (a.B19.C19.D19 !== null !is Boolean) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>a.B19.C19.D19 !== null !is Boolean<!>) {
|
||||
a.B19.C19.D19
|
||||
a.B19.C19.D19<!UNSAFE_CALL!>.<!>equals(null)
|
||||
a.B19.C19.D19.propT
|
||||
@@ -344,7 +344,7 @@ fun case_20(b: Boolean) {
|
||||
|
||||
// TESTCASE NUMBER: 21
|
||||
fun case_21() {
|
||||
if (EnumClassWithNullableProperty.B.prop_1 !== null is Boolean == <!USELESS_IS_CHECK!>true !is Boolean<!> != true) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>EnumClassWithNullableProperty.B.prop_1 !== null is Boolean<!> == <!USELESS_IS_CHECK!>true !is Boolean<!> != true) {
|
||||
EnumClassWithNullableProperty.B.prop_1
|
||||
EnumClassWithNullableProperty.B.prop_1<!UNSAFE_CALL!>.<!>equals(null)
|
||||
EnumClassWithNullableProperty.B.prop_1.propT
|
||||
@@ -360,7 +360,7 @@ fun case_21() {
|
||||
|
||||
// TESTCASE NUMBER: 22
|
||||
fun case_22(a: (() -> Unit)?) {
|
||||
if (a != null !is Boolean) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>a != null !is Boolean<!>) {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Unit")!><!UNSAFE_IMPLICIT_INVOKE_CALL!>a<!>()<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Unit")!><!UNSAFE_IMPLICIT_INVOKE_CALL!>a<!>()<!>.equals(null)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Unit")!><!UNSAFE_IMPLICIT_INVOKE_CALL!>a<!>()<!>.propT
|
||||
@@ -376,7 +376,7 @@ fun case_22(a: (() -> Unit)?) {
|
||||
|
||||
// TESTCASE NUMBER: 23
|
||||
fun case_23(a: ((Float) -> Int?)?, b: Float?) {
|
||||
if (a != null !is Boolean && b !== null is Boolean) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>a != null !is Boolean<!> && <!EQUALITY_NOT_APPLICABLE!>b !== null is Boolean<!>) {
|
||||
val x = <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!><!UNSAFE_IMPLICIT_INVOKE_CALL!>a<!>(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Float?")!>b<!>)<!>
|
||||
if (x != null) {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
@@ -395,7 +395,7 @@ fun case_23(a: ((Float) -> Int?)?, b: Float?) {
|
||||
|
||||
// TESTCASE NUMBER: 24
|
||||
fun case_24(a: ((() -> Unit) -> Unit)?, b: (() -> Unit)?) =
|
||||
if (a !== null is Boolean && b !== null !is Boolean) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>a !== null is Boolean<!> && <!EQUALITY_NOT_APPLICABLE!>b !== null !is Boolean<!>) {
|
||||
<!UNSAFE_IMPLICIT_INVOKE_CALL!>a<!>(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function0<kotlin.Unit>?")!>b<!>)
|
||||
<!UNSAFE_IMPLICIT_INVOKE_CALL!>a<!>(b)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function0<kotlin.Unit>?")!>b<!><!UNSAFE_CALL!>.<!>equals(null)
|
||||
|
||||
@@ -577,7 +577,7 @@ fun case_29(x: Boolean) {
|
||||
if (false || false || false || false || y !== v) {
|
||||
val t = <!DEBUG_INFO_EXPRESSION_TYPE("<anonymous>?")!><!UNSAFE_IMPLICIT_INVOKE_CALL!>y<!>()<!>
|
||||
|
||||
if (z !== t || false) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE_WARNING!>z !== t<!> || false) {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("<anonymous>?")!>t<!><!UNSAFE_CALL!>.<!>a
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("<anonymous>?")!>t<!><!UNSAFE_CALL!>.<!>equals(null)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("<anonymous>?")!>t<!>.propT
|
||||
|
||||
@@ -221,6 +221,7 @@ enum class LanguageFeature(
|
||||
),
|
||||
MultiPlatformProjects(sinceVersion = null, defaultState = State.DISABLED),
|
||||
InlineClasses(KOTLIN_1_3, defaultState = State.ENABLED_WITH_WARNING, kind = UNSTABLE_FEATURE),
|
||||
ProhibitComparisonOfIncompatibleClasses(sinceVersion = null, kind = BUG_FIX, defaultState = State.DISABLED),
|
||||
|
||||
;
|
||||
|
||||
|
||||
+42
@@ -1112,6 +1112,22 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.INCOMPATIBLE_TYPES) { firDiagnostic ->
|
||||
IncompatibleTypesImpl(
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.INCOMPATIBLE_TYPES_WARNING) { firDiagnostic ->
|
||||
IncompatibleTypesWarningImpl(
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic ->
|
||||
ExtensionInClassReferenceNotAllowedImpl(
|
||||
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration),
|
||||
@@ -2020,6 +2036,32 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EQUALITY_NOT_APPLICABLE) { firDiagnostic ->
|
||||
EqualityNotApplicableImpl(
|
||||
firDiagnostic.a,
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.c),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EQUALITY_NOT_APPLICABLE_WARNING) { firDiagnostic ->
|
||||
EqualityNotApplicableWarningImpl(
|
||||
firDiagnostic.a,
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.c),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.INCOMPATIBLE_ENUM_COMPARISON_ERROR) { firDiagnostic ->
|
||||
IncompatibleEnumComparisonErrorImpl(
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.TOPLEVEL_TYPEALIASES_ONLY) { firDiagnostic ->
|
||||
ToplevelTypealiasesOnlyImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
|
||||
+32
@@ -785,6 +785,18 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = DynamicUpperBound::class
|
||||
}
|
||||
|
||||
abstract class IncompatibleTypes : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = IncompatibleTypes::class
|
||||
abstract val typeA: KtType
|
||||
abstract val typeB: KtType
|
||||
}
|
||||
|
||||
abstract class IncompatibleTypesWarning : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = IncompatibleTypesWarning::class
|
||||
abstract val typeA: KtType
|
||||
abstract val typeB: KtType
|
||||
}
|
||||
|
||||
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
|
||||
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
|
||||
abstract val referencedDeclaration: KtCallableSymbol
|
||||
@@ -1413,6 +1425,26 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = UnderscoreUsageWithoutBackticks::class
|
||||
}
|
||||
|
||||
abstract class EqualityNotApplicable : KtFirDiagnostic<KtBinaryExpression>() {
|
||||
override val diagnosticClass get() = EqualityNotApplicable::class
|
||||
abstract val operator: String
|
||||
abstract val leftType: KtType
|
||||
abstract val rightType: KtType
|
||||
}
|
||||
|
||||
abstract class EqualityNotApplicableWarning : KtFirDiagnostic<KtBinaryExpression>() {
|
||||
override val diagnosticClass get() = EqualityNotApplicableWarning::class
|
||||
abstract val operator: String
|
||||
abstract val leftType: KtType
|
||||
abstract val rightType: KtType
|
||||
}
|
||||
|
||||
abstract class IncompatibleEnumComparisonError : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = IncompatibleEnumComparisonError::class
|
||||
abstract val leftType: KtType
|
||||
abstract val rightType: KtType
|
||||
}
|
||||
|
||||
abstract class ToplevelTypealiasesOnly : KtFirDiagnostic<KtTypeAlias>() {
|
||||
override val diagnosticClass get() = ToplevelTypealiasesOnly::class
|
||||
}
|
||||
|
||||
+47
@@ -1269,6 +1269,24 @@ internal class DynamicUpperBoundImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class IncompatibleTypesImpl(
|
||||
override val typeA: KtType,
|
||||
override val typeB: KtType,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.IncompatibleTypes(), KtAbstractFirDiagnostic<KtElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class IncompatibleTypesWarningImpl(
|
||||
override val typeA: KtType,
|
||||
override val typeB: KtType,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.IncompatibleTypesWarning(), KtAbstractFirDiagnostic<KtElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExtensionInClassReferenceNotAllowedImpl(
|
||||
override val referencedDeclaration: KtCallableSymbol,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
@@ -2293,6 +2311,35 @@ internal class UnderscoreUsageWithoutBackticksImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class EqualityNotApplicableImpl(
|
||||
override val operator: String,
|
||||
override val leftType: KtType,
|
||||
override val rightType: KtType,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.EqualityNotApplicable(), KtAbstractFirDiagnostic<KtBinaryExpression> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class EqualityNotApplicableWarningImpl(
|
||||
override val operator: String,
|
||||
override val leftType: KtType,
|
||||
override val rightType: KtType,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.EqualityNotApplicableWarning(), KtAbstractFirDiagnostic<KtBinaryExpression> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class IncompatibleEnumComparisonErrorImpl(
|
||||
override val leftType: KtType,
|
||||
override val rightType: KtType,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.IncompatibleEnumComparisonError(), KtAbstractFirDiagnostic<KtElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ToplevelTypealiasesOnlyImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
|
||||
@@ -13,10 +13,10 @@ fun f(): Unit {
|
||||
x == 1
|
||||
x != 1
|
||||
|
||||
A() == 1
|
||||
<error descr="[EQUALITY_NOT_APPLICABLE] Operator '==' cannot be applied to 'A' and 'kotlin/Int'">A() == 1</error>
|
||||
|
||||
x === "1"
|
||||
x !== "1"
|
||||
<error descr="[EQUALITY_NOT_APPLICABLE] Operator '===' cannot be applied to 'kotlin/Int' and 'kotlin/String'">x === "1"</error>
|
||||
<error descr="[EQUALITY_NOT_APPLICABLE] Operator '!==' cannot be applied to 'kotlin/Int' and 'kotlin/String'">x !== "1"</error>
|
||||
|
||||
x === 1
|
||||
x !== 1
|
||||
|
||||
Vendored
+3
-3
@@ -7,7 +7,7 @@ fun foo() : Int {
|
||||
is String -> 1
|
||||
!is Int -> 1
|
||||
is Any? -> 1
|
||||
s -> 1
|
||||
<error descr="[INCOMPATIBLE_TYPES] Incompatible types: kotlin/Int and kotlin/String">s</error> -> 1
|
||||
1 -> 1
|
||||
1 <error descr="[OVERLOAD_RESOLUTION_AMBIGUITY] Overload resolution ambiguity between candidates: [kotlin/Int.plus, kotlin/Int.plus, kotlin/Int.plus, ...]">+</error> <error descr="[UNRESOLVED_REFERENCE] Unresolved reference: a">a</error> -> 1
|
||||
in 1..<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: a">a</error> -> 1
|
||||
@@ -25,8 +25,8 @@ fun test() {
|
||||
val s = "";
|
||||
|
||||
when (x) {
|
||||
s -> 1
|
||||
"" -> 1
|
||||
<error descr="[INCOMPATIBLE_TYPES] Incompatible types: kotlin/Int and kotlin/String">s</error> -> 1
|
||||
<error descr="[INCOMPATIBLE_TYPES] Incompatible types: kotlin/Int and kotlin/String">""</error> -> 1
|
||||
x -> 1
|
||||
1 -> 1
|
||||
else -> 1
|
||||
|
||||
Reference in New Issue
Block a user