SenselessComparisonChecker used for @NotNull values from Java

This commit is contained in:
Andrey Breslav
2015-02-05 19:18:10 +03:00
parent d31b9be5fb
commit fcb6055913
10 changed files with 141 additions and 15 deletions
@@ -62,6 +62,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
import org.jetbrains.kotlin.psi.JetPostfixExpression
import org.jetbrains.kotlin.psi.JetBinaryExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker
public object KotlinJvmCheckerProvider : AdditionalCheckerProvider(
annotationCheckers = listOf(PlatformStaticAnnotationChecker(), LocalFunInlineChecker(), ReifiedTypeParameterAnnotationChecker(), NativeFunChecker()),
@@ -202,7 +203,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
if (expression.getOperationToken() == JetTokens.EXCLEXCL) {
val baseExpression = expression.getBaseExpression()
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
warnIfNotNull(
doIfNotNull(
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()),
c
) {
@@ -210,25 +211,42 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
}
}
is JetBinaryExpression ->
if (expression.getOperationToken() == JetTokens.ELVIS) {
when (expression.getOperationToken()) {
JetTokens.ELVIS -> {
val baseExpression = expression.getLeft()
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
warnIfNotNull(
doIfNotNull(
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()),
c
) {
c.trace.report(Errors.USELESS_ELVIS.on(expression.getOperationReference(), baseExpressionType))
}
}
JetTokens.EQEQ,
JetTokens.EXCLEQ,
JetTokens.EQEQEQ,
JetTokens.EXCLEQEQEQ -> {
if (expression.getLeft() != null && expression.getRight() != null) {
SenselessComparisonChecker.checkSenselessComparisonWithNull(
expression, expression.getLeft(), expression.getRight(), c.trace,
{ c.trace.get(BindingContext.EXPRESSION_TYPE, it) },
{
value ->
doIfNotNull(value, c) { Nullability.NOT_NULL } ?: Nullability.UNKNOWN
}
)
}
}
}
}
}
private fun warnIfNotNull(dataFlowValue: DataFlowValue, c: ResolutionContext<*>, reportWarning: () -> Unit) {
private fun <T: Any> doIfNotNull(dataFlowValue: DataFlowValue, c: ResolutionContext<*>, body: () -> T): T? {
if (c.dataFlowInfo.getNullability(dataFlowValue).canBeNull()
&& dataFlowValue.getType().mustNotBeNull() == NullabilityInformationSource.JAVA) {
reportWarning()
return body()
}
return null
}
override fun checkReceiver(
@@ -261,7 +279,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
}
else {
// TODO: Compiler bug
warnIfNotNull(dataFlowValue, c as ResolutionContext<*>) {
doIfNotNull(dataFlowValue, c as ResolutionContext<*>) {
c.trace.report(Errors.UNNECESSARY_SAFE_CALL.on(c.call.getCallOperationNode().getPsi(), receiverArgument.getType()))
}
}
@@ -1152,7 +1152,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
context.trace.report(EQUALITY_NOT_APPLICABLE.on(expression, expression.getOperationReference(), leftType, rightType));
}
SenselessComparisonChecker.checkSenselessComparisonWithNull(
expression, left, right, context,
expression, left, right, context.trace,
new Function1<JetExpression, JetType>() {
@Override
public JetType invoke(JetExpression expression) {
@@ -1164,8 +1164,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
public Nullability invoke(DataFlowValue value) {
return context.dataFlowInfo.getNullability(value);
}
}
);
});
}
}
}
@@ -24,13 +24,14 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
import org.jetbrains.kotlin.diagnostics.Errors
import kotlin.platform.platformStatic
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
import org.jetbrains.kotlin.resolve.BindingTrace
object SenselessComparisonChecker {
platformStatic fun checkSenselessComparisonWithNull(
expression: JetBinaryExpression,
left: JetExpression,
right: JetExpression,
context: ExpressionTypingContext,
trace: BindingTrace,
getType: (JetExpression) -> JetType?,
getNullability: (DataFlowValue) -> Nullability
) {
@@ -43,10 +44,10 @@ object SenselessComparisonChecker {
if (type == null || type.isError()) return
val operationSign = expression.getOperationReference()
val value = DataFlowValueFactory.createDataFlowValue(expr, type, context.trace.getBindingContext())
val nullability = getNullability(value)
val value = DataFlowValueFactory.createDataFlowValue(expr, type, trace.getBindingContext())
val equality = operationSign.getReferencedNameElementType() == JetTokens.EQEQ || operationSign.getReferencedNameElementType() == JetTokens.EQEQEQ
val nullability = getNullability(value)
val expressionIsAlways =
if (nullability == Nullability.NULL) equality
@@ -54,6 +55,6 @@ object SenselessComparisonChecker {
else if (nullability == Nullability.IMPOSSIBLE) false
else return
context.trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways))
trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways))
}
}
@@ -1,4 +1,4 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
// !DIAGNOSTICS: -UNUSED_VARIABLE -SENSELESS_COMPARISON
// FILE: p/J.java
package p;
@@ -1,3 +1,5 @@
// !DIAGNOSTICS: -SENSELESS_COMPARISON
// FILE: p/J.java
package p;
@@ -0,0 +1,45 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
public static J staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
val a: Any? = null
if (<!SENSELESS_COMPARISON!>platformNN != null<!>) {}
if (<!SENSELESS_COMPARISON!>null != platformNN<!>) {}
if (<!SENSELESS_COMPARISON!>platformNN == null<!>) {}
if (<!SENSELESS_COMPARISON!>null == platformNN<!>) {}
if (a != null && platformNN != a) {}
if (platformN != null) {}
if (platformN == null) {}
if (a == null && platformN == a) {}
if (platformJ != null) {}
if (platformJ == null) {}
if (a == null && platformJ == a) {}
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,43 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
public static J staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
val a: Any? = null
if (<!SENSELESS_COMPARISON!>platformNN !== null<!>) {}
if (<!SENSELESS_COMPARISON!>null !== platformNN<!>) {}
if (<!SENSELESS_COMPARISON!>platformNN === null<!>) {}
if (<!SENSELESS_COMPARISON!>null === platformNN<!>) {}
if (platformN !== null) {}
if (platformN === null) {}
if (a === null && platformN === a) {}
if (platformJ !== null) {}
if (platformJ === null) {}
if (a === null && platformJ === a) {}
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -8664,6 +8664,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("senselessComparisonEquals.kt")
public void testSenselessComparisonEquals() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonEquals.kt");
doTest(fileName);
}
@TestMetadata("senselessComparisonIdentityEquals.kt")
public void testSenselessComparisonIdentityEquals() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonIdentityEquals.kt");
doTest(fileName);
}
@TestMetadata("throw.kt")
public void testThrow() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/throw.kt");