Warn for unnecessary (!!) assertion after method with generics

#KT-12276 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2017-04-03 02:37:43 +03:00
parent 4aa808b250
commit 3cdf6c898a
4 changed files with 46 additions and 9 deletions
@@ -892,7 +892,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return baseTypeInfo;
}
DataFlowInfo dataFlowInfo = baseTypeInfo.getDataFlowInfo();
if (isKnownToBeNotNull(baseExpression, context) && !baseType.isError()) {
if (isKnownToBeNotNull(baseExpression, baseType, context) && (!baseType.isError() || ErrorUtils.isUninferredParameter(baseType))) {
context.trace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, TypeUtils.makeNotNullable(baseType)));
}
else {
@@ -935,14 +935,15 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return facade.getTypeInfo(baseExpression, context, isStatement);
}
private static boolean isKnownToBeNotNull(KtExpression expression, ExpressionTypingContext context) {
KotlinType type = context.trace.getType(expression);
assert type != null : "This method is only supposed to be called when the type is not null";
return isKnownToBeNotNull(expression, type, context);
}
private static boolean isKnownToBeNotNull(
@NotNull KtExpression expression,
@Nullable KotlinType ktType,
@NotNull ExpressionTypingContext context
) {
if (ktType == null) return false;
if (!TypeUtils.isNullableType(ktType)) return true;
private static boolean isKnownToBeNotNull(KtExpression expression, KotlinType jetType, ExpressionTypingContext context) {
DataFlowValue dataFlowValue = createDataFlowValue(expression, jetType, context);
DataFlowValue dataFlowValue = createDataFlowValue(expression, ktType, context);
return context.dataFlowInfo.getStableNullability(dataFlowValue) == Nullability.NOT_NULL;
}
@@ -1272,7 +1273,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
assert leftTypeInfo != null : "Left expression was not processed: " + expression;
KotlinType leftType = leftTypeInfo.getType();
if (leftType != null && (!TypeUtils.isNullableType(leftType) || isKnownToBeNotNull(left, leftType, context))) {
if (isKnownToBeNotNull(left, leftType, context)) {
context.trace.report(USELESS_ELVIS.on(expression, leftType));
}
else if (KtPsiUtil.isNullConstant(right) && leftType != null && !FlexibleTypesKt.isNullabilityFlexible(leftType)) {
@@ -0,0 +1,23 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER, -SENSELESS_COMPARISON, -DEBUG_INFO_SMARTCAST
fun takeNotNull(s: String) {}
fun <T> notNull(): T = TODO()
fun <T> nullable(): T? = null
fun <T> dependOn(x: T) = x
fun test() {
takeNotNull(notNull()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
takeNotNull(nullable()!!)
var x: String? = null
takeNotNull(dependOn(x)!!)
takeNotNull(dependOn(dependOn(x))!!)
takeNotNull(dependOn(dependOn(x)!!))
takeNotNull(dependOn(dependOn(x!!)))
if (x != null) {
takeNotNull(dependOn(x)<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
takeNotNull(dependOn(dependOn(x))<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
takeNotNull(dependOn(dependOn(x)<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>))
}
}
@@ -0,0 +1,7 @@
package
public fun </*0*/ T> dependOn(/*0*/ x: T): T
public fun </*0*/ T> notNull(): T
public fun </*0*/ T> nullable(): T?
public fun takeNotNull(/*0*/ s: kotlin.String): kotlin.Unit
public fun test(): kotlin.Unit
@@ -13701,6 +13701,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/smartCastsAndBooleanExpressions.kt");
doTest(fileName);
}
@TestMetadata("unnecessaryNotNullAssertion.kt")
public void testUnnecessaryNotNullAssertion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/nullableTypes")