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:
Tianyu Geng
2021-07-28 13:29:37 -07:00
committed by teamcityserver
parent c7272f6986
commit 263b876e6e
16 changed files with 97 additions and 98 deletions
@@ -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")
}
@@ -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
@@ -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
}
}