Always null detection

This commit is contained in:
Mikhail Glukhikh
2015-11-06 17:54:05 +03:00
parent ed1c94d0f9
commit 2d9fbf5696
40 changed files with 140 additions and 75 deletions
@@ -657,6 +657,7 @@ public interface Errors {
DiagnosticFactory1<KtExpression, KotlinType> IMPLICIT_CAST_TO_UNIT_OR_ANY = DiagnosticFactory1.create(WARNING);
DiagnosticFactory3<KtExpression, KotlinType, String, String> SMARTCAST_IMPOSSIBLE = DiagnosticFactory3.create(ERROR);
DiagnosticFactory0<KtExpression> ALWAYS_NULL = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtNullableType> USELESS_NULLABLE_CHECK = DiagnosticFactory0.create(WARNING, NULLABLE_TYPE);
@@ -481,6 +481,7 @@ public class DefaultErrorMessages {
}, DECLARATION_NAME);
MAP.put(SMARTCAST_IMPOSSIBLE,
"Smart cast to ''{0}'' is impossible, because ''{1}'' is a {2}", RENDER_TYPE, STRING, STRING);
MAP.put(ALWAYS_NULL, "The result of the expression is always null");
MAP.put(MISSING_CONSTRUCTOR_KEYWORD, "Use 'constructor' keyword after modifiers of primary constructor");
@@ -97,12 +97,28 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
super(facade);
}
private static boolean isLValue(@NotNull KtSimpleNameExpression expression) {
PsiElement parent = PsiTreeUtil.skipParentsOfType(expression, KtParenthesizedExpression.class);
if (!(parent instanceof KtBinaryExpression)) return false;
KtBinaryExpression binaryExpression = (KtBinaryExpression) parent;
if (!KtTokens.ALL_ASSIGNMENTS.contains(binaryExpression.getOperationToken())) return false;
return PsiTreeUtil.isAncestor(binaryExpression.getLeft(), expression, false);
}
@Override
public KotlinTypeInfo visitSimpleNameExpression(@NotNull KtSimpleNameExpression expression, ExpressionTypingContext context) {
// TODO : other members
// TODO : type substitutions???
CallExpressionResolver callExpressionResolver = components.callExpressionResolver;
KotlinTypeInfo typeInfo = callExpressionResolver.getSimpleNameExpressionTypeInfo(expression, NO_RECEIVER, null, context);
if (typeInfo.getType() != null && !typeInfo.getType().isError() && !isLValue(expression)) {
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, typeInfo.getType(), context);
Nullability nullability = context.dataFlowInfo.getPredictableNullability(dataFlowValue);
if (!nullability.canBeNonNull() && nullability.canBeNull()) {
context.trace.report(ALWAYS_NULL.on(expression));
}
}
return components.dataFlowAnalyzer.checkType(typeInfo, expression, context); // TODO : Extensions to this
}
@@ -50,7 +50,7 @@ object SenselessComparisonChecker {
val nullability = getNullability(value)
val expressionIsAlways =
if (nullability == Nullability.NULL) equality
if (nullability == Nullability.NULL) return
else if (nullability == Nullability.NOT_NULL) !equality
else if (nullability == Nullability.IMPOSSIBLE) false
else return