SenselessComparisonChecker used for @NotNull values from Java
This commit is contained in:
+25
-7
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -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;
|
||||
|
||||
|
||||
+45
@@ -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) {}
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
internal fun test(): kotlin.Unit
|
||||
+43
@@ -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) {}
|
||||
}
|
||||
|
||||
+3
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user