diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 22d04b2ac66..3060c40d25a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -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)) { diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.kt new file mode 100644 index 00000000000..01c1b825896 --- /dev/null +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.kt @@ -0,0 +1,23 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER, -SENSELESS_COMPARISON, -DEBUG_INFO_SMARTCAST + +fun takeNotNull(s: String) {} +fun notNull(): T = TODO() +fun nullable(): T? = null +fun dependOn(x: T) = x + +fun test() { + takeNotNull(notNull()!!) + 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)!!) + takeNotNull(dependOn(dependOn(x))!!) + takeNotNull(dependOn(dependOn(x)!!)) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.txt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.txt new file mode 100644 index 00000000000..6c9603d2214 --- /dev/null +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.txt @@ -0,0 +1,7 @@ +package + +public fun dependOn(/*0*/ x: T): T +public fun notNull(): T +public fun nullable(): T? +public fun takeNotNull(/*0*/ s: kotlin.String): kotlin.Unit +public fun test(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 7b37596199f..6f9b67299f0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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")