Detect redundant 'is' check
#KT-14187 Fixed
This commit is contained in:
committed by
Mikhail Zarechenskiy
parent
768e0fa738
commit
cd24adac32
@@ -826,6 +826,8 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtExpression> ALWAYS_NULL = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<KtNullableType> USELESS_NULLABLE_CHECK = DiagnosticFactory0.create(WARNING, NULLABLE_TYPE);
|
||||
DiagnosticFactory1<KtElement, Boolean> USELESS_IS_CHECK = DiagnosticFactory1.create(WARNING);
|
||||
|
||||
|
||||
// Properties / locals
|
||||
|
||||
|
||||
+1
@@ -396,6 +396,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(IS_ENUM_ENTRY, "'is' over enum entry is not allowed, use comparison instead");
|
||||
MAP.put(ENUM_ENTRY_AS_TYPE, "Use of enum entry names as types is not allowed, use enum type instead");
|
||||
MAP.put(USELESS_NULLABLE_CHECK, "Non-null type is checked for instance of nullable type");
|
||||
MAP.put(USELESS_IS_CHECK, "Check for instance is always ''{0}''", TO_STRING);
|
||||
MAP.put(WRONG_SETTER_PARAMETER_TYPE, "Setter parameter type must be equal to the type of the property, i.e. ''{0}''", RENDER_TYPE, RENDER_TYPE);
|
||||
MAP.put(WRONG_GETTER_RETURN_TYPE, "Getter return type must be equal to the type of the property, i.e. ''{0}''", RENDER_TYPE, RENDER_TYPE);
|
||||
MAP.put(WRONG_SETTER_RETURN_TYPE, "Setter return type must be Unit");
|
||||
|
||||
+16
-4
@@ -39,6 +39,8 @@ import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.*
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
|
||||
import org.jetbrains.kotlin.types.typeUtil.containsError
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import java.util.*
|
||||
|
||||
class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) {
|
||||
@@ -51,7 +53,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
val typeReference = expression.typeReference
|
||||
if (typeReference != null && knownType != null) {
|
||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, context)
|
||||
val conditionInfo = checkTypeForIs(context, knownType, typeReference, dataFlowValue).thenInfo
|
||||
val conditionInfo = checkTypeForIs(context, expression, expression.isNegated, knownType, typeReference, dataFlowValue).thenInfo
|
||||
val newDataFlowInfo = conditionInfo.and(typeInfo.dataFlowInfo)
|
||||
context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo)
|
||||
}
|
||||
@@ -342,7 +344,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
}
|
||||
val typeReference = condition.typeReference
|
||||
if (typeReference != null) {
|
||||
val result = checkTypeForIs(context, subjectType, typeReference, subjectDataFlowValue)
|
||||
val result = checkTypeForIs(context, condition, condition.isNegated, subjectType, typeReference, subjectDataFlowValue)
|
||||
if (condition.isNegated) {
|
||||
newDataFlowInfo = ConditionalDataFlowInfo(result.elseInfo, result.thenInfo)
|
||||
}
|
||||
@@ -417,6 +419,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
|
||||
private fun checkTypeForIs(
|
||||
context: ExpressionTypingContext,
|
||||
isCheck: KtElement,
|
||||
negated: Boolean,
|
||||
subjectType: KotlinType,
|
||||
typeReferenceAfterIs: KtTypeReference,
|
||||
subjectDataFlowValue: DataFlowValue
|
||||
@@ -432,13 +436,21 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
if (targetDescriptor != null && DescriptorUtils.isEnumEntry(targetDescriptor)) {
|
||||
context.trace.report(IS_ENUM_ENTRY.on(typeReferenceAfterIs))
|
||||
}
|
||||
|
||||
if (!TypeUtils.isNullableType(subjectType) && targetType.isMarkedNullable) {
|
||||
val subjectTypeHasError = subjectType.containsError()
|
||||
if (!subjectTypeHasError && !TypeUtils.isNullableType(subjectType) && targetType.isMarkedNullable) {
|
||||
val element = typeReferenceAfterIs.typeElement
|
||||
assert(element is KtNullableType) { "element must be instance of " + KtNullableType::class.java.name }
|
||||
context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(element as KtNullableType))
|
||||
}
|
||||
checkTypeCompatibility(context, targetType, subjectType, typeReferenceAfterIs)
|
||||
if (!subjectTypeHasError && !targetType.containsError()) {
|
||||
val possibleTypes = hashSetOf(subjectType)
|
||||
possibleTypes.addAll(context.dataFlowInfo.getStableTypes(subjectDataFlowValue))
|
||||
val intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, possibleTypes.map { it.upperIfFlexible() })
|
||||
if (intersection?.isSubtypeOf(targetType) ?: false) {
|
||||
context.trace.report(Errors.USELESS_IS_CHECK.on(isCheck, !negated))
|
||||
}
|
||||
}
|
||||
if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, KotlinTypeChecker.DEFAULT)) {
|
||||
context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user