FIR: extends scope of SENSELESS_COMPARISON
FE1.0 only reports SENSELESS_COMPARISON if one of the operand is `null`. This change makes FIR reports also in case one of the operand has type `Nothing?`. In addition, fix handling of type alias in ConeTypeContext#isNullableType
This commit is contained in:
committed by
teamcityserver
parent
c7272f6986
commit
263b876e6e
+1
-1
@@ -948,7 +948,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
parameter<Set<FirSourceElement>>("reachable")
|
||||
parameter<Set<FirSourceElement>>("unreachable")
|
||||
}
|
||||
val SENSELESS_COMPARISON by warning<KtBinaryExpression> {
|
||||
val SENSELESS_COMPARISON by warning<KtExpression> {
|
||||
parameter<FirExpression>("expression")
|
||||
parameter<Boolean>("compareResult")
|
||||
}
|
||||
|
||||
+1
-1
@@ -507,7 +507,7 @@ object FirErrors {
|
||||
val VARIABLE_WITH_NO_TYPE_NO_INITIALIZER by error0<KtVariableDeclaration>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
val INITIALIZATION_BEFORE_DECLARATION by error1<KtExpression, FirBasedSymbol<*>>()
|
||||
val UNREACHABLE_CODE by warning2<KtElement, Set<FirSourceElement>, Set<FirSourceElement>>(SourceElementPositioningStrategies.UNREACHABLE_CODE)
|
||||
val SENSELESS_COMPARISON by warning2<KtBinaryExpression, FirExpression, Boolean>()
|
||||
val SENSELESS_COMPARISON by warning2<KtExpression, FirExpression, Boolean>()
|
||||
val SENSELESS_NULL_IN_WHEN by warning0<KtElement>()
|
||||
|
||||
// Nullability
|
||||
|
||||
+17
-20
@@ -16,8 +16,6 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.expressions.FirEqualityOperatorCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcastToNull
|
||||
import org.jetbrains.kotlin.fir.expressions.FirOperation
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
|
||||
@@ -29,21 +27,19 @@ object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
|
||||
override fun check(expression: FirEqualityOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val arguments = expression.argumentList.arguments
|
||||
if (arguments.size != 2) return
|
||||
val lExpr = arguments[0]
|
||||
val rExpr = arguments[1]
|
||||
checkCompatibility(lExpr, rExpr, context, expression, reporter)
|
||||
checkSensibleness(lExpr, rExpr, context, expression, reporter)
|
||||
val lType = arguments[0].typeRef.coneType
|
||||
val rType = arguments[1].typeRef.coneType
|
||||
checkCompatibility(lType, rType, context, expression, reporter)
|
||||
checkSensibleness(lType, rType, context, expression, reporter)
|
||||
}
|
||||
|
||||
private fun checkCompatibility(
|
||||
lExpr: FirExpression,
|
||||
rExpr: FirExpression,
|
||||
lType: ConeKotlinType,
|
||||
rType: ConeKotlinType,
|
||||
context: CheckerContext,
|
||||
expression: FirEqualityOperatorCall,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val lType = lExpr.typeRef.coneType
|
||||
val rType = rExpr.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
|
||||
@@ -115,32 +111,33 @@ object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
|
||||
}
|
||||
|
||||
private fun checkSensibleness(
|
||||
lExpr: FirExpression,
|
||||
rExpr: FirExpression,
|
||||
lType: ConeKotlinType,
|
||||
rType: ConeKotlinType,
|
||||
context: CheckerContext,
|
||||
expression: FirEqualityOperatorCall,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val expressionComparedWithNull = when {
|
||||
lExpr.isNullLiteral -> rExpr
|
||||
rExpr.isNullLiteral -> lExpr
|
||||
val type = when {
|
||||
rType.isNullableNothing -> lType
|
||||
lType.isNullableNothing -> rType
|
||||
else -> return
|
||||
}
|
||||
val type = expressionComparedWithNull.typeRef.coneType
|
||||
if (type is ConeKotlinErrorType) return
|
||||
val isPositiveCompare = expression.operation == FirOperation.EQ || expression.operation == FirOperation.IDENTITY
|
||||
val compareResult = with(context.session.typeContext) {
|
||||
when {
|
||||
// `null` literal has type `Nothing?`
|
||||
type.isNullableNothing || (expressionComparedWithNull is FirExpressionWithSmartcastToNull && expressionComparedWithNull.isStable) -> isPositiveCompare
|
||||
type.isNullableNothing -> isPositiveCompare
|
||||
!type.isNullableType() -> !isPositiveCompare
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
if (expression.source?.elementType == KtNodeTypes.BINARY_EXPRESSION) {
|
||||
reporter.reportOn(expression.source, FirErrors.SENSELESS_COMPARISON, expression, compareResult, context)
|
||||
} else {
|
||||
// We only report `SENSELESS_NULL_IN_WHEN` if `lType = type` because `lType` is the type of the when subject. This diagnostic is
|
||||
// only intended for cases where the branch condition contains a null.
|
||||
if (expression.source?.elementType != KtNodeTypes.BINARY_EXPRESSION && type === lType) {
|
||||
reporter.reportOn(expression.source, FirErrors.SENSELESS_NULL_IN_WHEN, context)
|
||||
} else {
|
||||
reporter.reportOn(expression.source, FirErrors.SENSELESS_COMPARISON, expression, compareResult, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,16 @@ import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirVarargArgumentsExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.arguments
|
||||
import org.jetbrains.kotlin.fir.resolve.correspondingSupertypesCache
|
||||
import org.jetbrains.kotlin.fir.resolve.directExpansionType
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.firUnsafe
|
||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
@@ -443,6 +444,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
|
||||
}
|
||||
}
|
||||
is ConeIntersectionType -> intersectedTypes.all { it.isNullableType() }
|
||||
is ConeClassLikeType -> directExpansionType(session)?.isNullableType() ?: false
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user