Prevent early deleting of not-null assertions

Before val to var conversion applied, smart-cast can cause
dropping not-null assertion, which will be required when val will
become var
This commit is contained in:
Simon Ogorodnik
2017-05-29 15:24:23 +03:00
parent e41c027c9a
commit a6e153312c
5 changed files with 89 additions and 5 deletions
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.mapToIndex
import java.util.*
@@ -114,11 +115,6 @@ object J2KPostProcessingRegistrar {
fix.invoke()
}
registerDiagnosticBasedProcessing<KtSimpleNameExpression>(Errors.UNNECESSARY_NOT_NULL_ASSERTION) { element, _ ->
val exclExclExpr = element.parent as KtUnaryExpression
exclExclExpr.replace(exclExclExpr.baseExpression!!)
}
registerDiagnosticBasedProcessingFactory(
Errors.VAL_REASSIGNMENT, Errors.CAPTURED_VAL_INITIALIZATION, Errors.CAPTURED_MEMBER_VAL_INITIALIZATION
) {
@@ -136,6 +132,15 @@ object J2KPostProcessingRegistrar {
}
}
registerDiagnosticBasedProcessing<KtSimpleNameExpression>(Errors.UNNECESSARY_NOT_NULL_ASSERTION) { element, _ ->
val exclExclExpr = element.parent as KtUnaryExpression
val baseExpression = exclExclExpr.baseExpression!!
val context = baseExpression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
if (context.diagnostics.forElement(element).any { it.factory == Errors.UNNECESSARY_NOT_NULL_ASSERTION }) {
exclExclExpr.replace(baseExpression)
}
}
processingsToPriorityMap.putAll(_processings.mapToIndex())
}
@@ -0,0 +1,35 @@
import org.jetbrains.annotations.*;
class A {
/* rare nullable, handle with caution */
public String nullableString() {
if (Math.random() > 0.999) {
return "a string";
}
return null;
}
public void takesNotNullString(@NotNull String s) {
System.out.println(s.substring(1));
}
public void aVoid() {
String aString;
if (nullableString() != null) {
aString = nullableString();
if (aString != null) {
for (int i = 0; i < 10; i++) {
takesNotNullString(aString); // Bang-bang here
aString = nullableString();
}
}
else {
aString = "aaa";
}
}
else {
aString = "bbbb";
}
}
}
@@ -0,0 +1,32 @@
import org.jetbrains.annotations.*
internal class A {
/* rare nullable, handle with caution */
fun nullableString(): String? {
return if (Math.random() > 0.999) {
"a string"
} else null
}
fun takesNotNullString(s: String) {
println(s.substring(1))
}
fun aVoid() {
var aString: String?
if (nullableString() != null) {
aString = nullableString()
if (aString != null) {
for (i in 0..9) {
takesNotNullString(aString!!) // Bang-bang here
aString = nullableString()
}
} else {
aString = "aaa"
}
} else {
aString = "bbbb"
}
}
}
@@ -3779,6 +3779,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("LocalValReassignment.java")
public void testLocalValReassignment() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/nullability/LocalValReassignment.java");
doTest(fileName);
}
@TestMetadata("MethodInvokedWithNullArg.java")
public void testMethodInvokedWithNullArg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/nullability/MethodInvokedWithNullArg.java");
@@ -3779,6 +3779,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("LocalValReassignment.java")
public void testLocalValReassignment() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/nullability/LocalValReassignment.java");
doTest(fileName);
}
@TestMetadata("MethodInvokedWithNullArg.java")
public void testMethodInvokedWithNullArg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/nullability/MethodInvokedWithNullArg.java");