Java: Update variable nullability only when DFA is completed successfully (IDEA-188416)

This commit is contained in:
Pavel Dolgov
2018-05-18 13:17:08 +03:00
parent 7961637e71
commit ea0b3c7fa9
6 changed files with 87 additions and 5 deletions
@@ -60,22 +60,25 @@ public class DfaUtil {
});
}
@NotNull
@Nullable
public static Nullness checkNullness(@Nullable final PsiVariable variable, @Nullable final PsiElement context) {
return checkNullness(variable, context, null);
}
@NotNull
/**
* @return {@code null} means "can't get results of DFA"
*/
@Nullable
public static Nullness checkNullness(@Nullable final PsiVariable variable,
@Nullable final PsiElement context,
@Nullable final PsiElement outerBlock) {
if (variable == null || context == null) return Nullness.UNKNOWN;
if (variable == null || context == null) return null;
final PsiElement codeBlock = outerBlock == null ? DfaPsiUtil.getEnclosingCodeBlock(variable, context) : outerBlock;
Map<PsiElement, ValuableInstructionVisitor.PlaceResult> results = codeBlock == null ? null : getCachedPlaceResults(codeBlock);
ValuableInstructionVisitor.PlaceResult placeResult = results == null ? null : results.get(context);
if (placeResult == null) {
return Nullness.UNKNOWN;
return null;
}
if (placeResult.myNulls.contains(variable) && !placeResult.myNotNulls.contains(variable)) return Nullness.NULLABLE;
if (placeResult.myNotNulls.contains(variable) && !placeResult.myNulls.contains(variable)) return Nullness.NOT_NULL;
@@ -1720,6 +1720,9 @@ public class ExtractMethodProcessor implements MatchProvider {
PsiExpression initializer = ((PsiVariable)declaredElements[0]).getInitializer();
Nullness nullness = DfaUtil.checkNullness(variableCopy, initializer, bodyCopy);
if (nullness == null) {
return null;
}
return nullness == Nullness.NOT_NULL;
}
catch (IncorrectOperationException ignore) {
@@ -67,7 +67,7 @@ public class JavaSliceNullnessAnalyzer extends SliceNullnessAnalyzerBase {
if (value instanceof PsiLocalVariable || value instanceof PsiParameter) {
Nullness result = DfaUtil.checkNullness((PsiVariable)value, context);
if (result != Nullness.UNKNOWN) {
if (result != null && result != Nullness.UNKNOWN) {
return result;
}
}
@@ -0,0 +1,29 @@
import org.jetbrains.annotations.NotNull;
class TooComplexCode {
static class X { @NotNull X get() { return this; }}
static class A extends X { @NotNull X get() { return new B(); }}
static class B extends X { @NotNull X get() { return new C(); }}
static class C extends X { @NotNull X get() { return new A(); }}
void tooComplex(@NotNull X x) {
if (x instanceof A) {
<selection>X y = x.get();</selection>
if (y instanceof A) {
System.out.println("A A "+x+' '+y);
}
if (y instanceof B) {
System.out.println("A B "+x+' '+y);
}
}
if (x instanceof B) {
X y = x.get();
if (y instanceof A) {
System.out.println("B A "+x+' '+y);
}
if (y instanceof B) {
System.out.println("B B "+x+' '+y);
}
}
}
}
@@ -0,0 +1,34 @@
import org.jetbrains.annotations.NotNull;
class TooComplexCode {
static class X { @NotNull X get() { return this; }}
static class A extends X { @NotNull X get() { return new B(); }}
static class B extends X { @NotNull X get() { return new C(); }}
static class C extends X { @NotNull X get() { return new A(); }}
void tooComplex(@NotNull X x) {
if (x instanceof A) {
X y = newMethod(x);
if (y instanceof A) {
System.out.println("A A "+x+' '+y);
}
if (y instanceof B) {
System.out.println("A B "+x+' '+y);
}
}
if (x instanceof B) {
X y = newMethod(x);
if (y instanceof A) {
System.out.println("B A "+x+' '+y);
}
if (y instanceof B) {
System.out.println("B B "+x+' '+y);
}
}
}
@NotNull
private X newMethod(@NotNull X x) {
return x.get();
}
}
@@ -22,6 +22,8 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.registry.RegistryValue;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettings;
@@ -1093,6 +1095,17 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
doTest();
}
public void testNotNullArgumentTooComplexCode() throws Exception {
RegistryValue value = Registry.get("ide.dfa.state.limit");
int oldValue = value.asInteger();
try {
value.setValue(50);
doTest();
}finally {
value.setValue(oldValue);
}
}
public void testVariableInLoopWithConditionalBreak() throws Exception {
doTest();
}