mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
allow dfa inspection to treat all non-annotated members as nullable (IDEA-129898)
This commit is contained in:
+5
-1
@@ -64,6 +64,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
@NonNls private static final String SHORT_NAME = "ConstantConditions";
|
||||
public boolean SUGGEST_NULLABLE_ANNOTATIONS = false;
|
||||
public boolean DONT_REPORT_TRUE_ASSERT_STATEMENTS = false;
|
||||
public boolean TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = false;
|
||||
public boolean IGNORE_ASSERT_STATEMENTS = false;
|
||||
public boolean REPORT_CONSTANT_REFERENCE_VALUES = true;
|
||||
|
||||
@@ -82,6 +83,9 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
if (!REPORT_CONSTANT_REFERENCE_VALUES) {
|
||||
node.addContent(new Element("option").setAttribute("name", "REPORT_CONSTANT_REFERENCE_VALUES").setAttribute("value", "false"));
|
||||
}
|
||||
if (TREAT_UNKNOWN_MEMBERS_AS_NULLABLE) {
|
||||
node.addContent(new Element("option").setAttribute("name", "TREAT_UNKNOWN_MEMBERS_AS_NULLABLE").setAttribute("value", "true"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,7 +125,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
PsiClass containingClass = PsiTreeUtil.getParentOfType(scope, PsiClass.class);
|
||||
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass)) return;
|
||||
|
||||
final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(scope) {
|
||||
final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(scope, TREAT_UNKNOWN_MEMBERS_AS_NULLABLE) {
|
||||
@Override
|
||||
protected boolean shouldCheckTimeLimit() {
|
||||
if (!onTheFly) return false;
|
||||
|
||||
+5
-1
@@ -57,13 +57,17 @@ public class DataFlowRunner {
|
||||
public static final int MAX_STATES_PER_BRANCH = 300;
|
||||
|
||||
protected DataFlowRunner(PsiElement block) {
|
||||
this(block, false);
|
||||
}
|
||||
|
||||
protected DataFlowRunner(PsiElement block, boolean unknownMembersAreNullable) {
|
||||
PsiElement parentConstructor = PsiTreeUtil.findFirstParent(block, new Condition<PsiElement>() {
|
||||
@Override
|
||||
public boolean value(PsiElement psiElement) {
|
||||
return psiElement instanceof PsiMethod && ((PsiMethod)psiElement).isConstructor();
|
||||
}
|
||||
});
|
||||
myValueFactory = new DfaValueFactory(parentConstructor == null);
|
||||
myValueFactory = new DfaValueFactory(parentConstructor == null, unknownMembersAreNullable);
|
||||
}
|
||||
|
||||
public DfaValueFactory getFactory() {
|
||||
|
||||
+4
-1
@@ -41,7 +41,10 @@ public class StandardDataFlowRunner extends DataFlowRunner {
|
||||
private boolean myIsInMethod = false;
|
||||
|
||||
public StandardDataFlowRunner(PsiElement block) {
|
||||
super(block);
|
||||
this (block, false);
|
||||
}
|
||||
public StandardDataFlowRunner(PsiElement block, boolean unknownMembersAreNullable) {
|
||||
super(block, unknownMembersAreNullable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-1
@@ -343,7 +343,11 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
}
|
||||
|
||||
if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) {
|
||||
return factory.createTypeValue(type, myReturnTypeNullability.get(instruction));
|
||||
Nullness nullability = myReturnTypeNullability.get(instruction);
|
||||
if (nullability == Nullness.UNKNOWN && factory.isUnknownMembersAreNullable()) {
|
||||
nullability = Nullness.NULLABLE;
|
||||
}
|
||||
return factory.createTypeValue(type, nullability);
|
||||
}
|
||||
return DfaUnknownValue.getInstance();
|
||||
}
|
||||
|
||||
+7
-1
@@ -42,9 +42,11 @@ public class DfaValueFactory {
|
||||
private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myConvertibleCache = ContainerUtil.newHashMap();
|
||||
private final Map<PsiType, DfaPsiType> myDfaTypes = ContainerUtil.newHashMap();
|
||||
private final boolean myHonorFieldInitializers;
|
||||
private final boolean myUnknownMembersAreNullable;
|
||||
|
||||
public DfaValueFactory(boolean honorFieldInitializers) {
|
||||
public DfaValueFactory(boolean honorFieldInitializers, boolean unknownMembersAreNullable) {
|
||||
myHonorFieldInitializers = honorFieldInitializers;
|
||||
myUnknownMembersAreNullable = unknownMembersAreNullable;
|
||||
myValues.add(null);
|
||||
myVarFactory = new DfaVariableValue.Factory(this);
|
||||
myConstFactory = new DfaConstValue.Factory(this);
|
||||
@@ -58,6 +60,10 @@ public class DfaValueFactory {
|
||||
return myHonorFieldInitializers;
|
||||
}
|
||||
|
||||
public boolean isUnknownMembersAreNullable() {
|
||||
return myUnknownMembersAreNullable;
|
||||
}
|
||||
|
||||
public DfaValue createTypeValue(@Nullable PsiType type, Nullness nullability) {
|
||||
type = TypeConversionUtil.erasure(type);
|
||||
if (type == null) return DfaUnknownValue.getInstance();
|
||||
|
||||
+5
-3
@@ -165,6 +165,8 @@ public class DfaVariableValue extends DfaValue {
|
||||
return nullability;
|
||||
}
|
||||
|
||||
Nullness defaultNullability = var instanceof PsiMember && myFactory.isUnknownMembersAreNullable() ? Nullness.NULLABLE : Nullness.UNKNOWN;
|
||||
|
||||
if (var instanceof PsiParameter && var.getParent() instanceof PsiForeachStatement) {
|
||||
PsiExpression iteratedValue = ((PsiForeachStatement)var.getParent()).getIteratedValue();
|
||||
if (iteratedValue != null) {
|
||||
@@ -178,7 +180,7 @@ public class DfaVariableValue extends DfaValue {
|
||||
if (var instanceof PsiField && DfaPsiUtil.isFinalField((PsiVariable)var) && myFactory.isHonorFieldInitializers()) {
|
||||
List<PsiExpression> initializers = DfaPsiUtil.findAllConstructorInitializers((PsiField)var);
|
||||
if (initializers.isEmpty()) {
|
||||
return Nullness.UNKNOWN;
|
||||
return defaultNullability;
|
||||
}
|
||||
|
||||
boolean hasUnknowns = false;
|
||||
@@ -204,13 +206,13 @@ public class DfaVariableValue extends DfaValue {
|
||||
if (DfaPsiUtil.isInitializedNotNull((PsiField)var)) {
|
||||
return Nullness.NOT_NULL;
|
||||
}
|
||||
return Nullness.UNKNOWN;
|
||||
return defaultNullability;
|
||||
}
|
||||
|
||||
return Nullness.NOT_NULL;
|
||||
}
|
||||
|
||||
return Nullness.UNKNOWN;
|
||||
return defaultNullability;
|
||||
}
|
||||
|
||||
public boolean isFlushableByCalls() {
|
||||
|
||||
@@ -68,6 +68,7 @@ public class DataFlowInspection extends DataFlowInspectionBase {
|
||||
private final JCheckBox myReportConstantReferences;
|
||||
private final JCheckBox mySuggestNullables;
|
||||
private final JCheckBox myDontReportTrueAsserts;
|
||||
private final JCheckBox myTreatUnknownMembersAsNullable;
|
||||
|
||||
private OptionsPanel() {
|
||||
super(new GridBagLayout());
|
||||
@@ -116,6 +117,15 @@ public class DataFlowInspection extends DataFlowInspectionBase {
|
||||
}
|
||||
});
|
||||
|
||||
myTreatUnknownMembersAsNullable = new JCheckBox("Treat non-annotated methods and fields as @Nullable");
|
||||
myTreatUnknownMembersAsNullable.setSelected(TREAT_UNKNOWN_MEMBERS_AS_NULLABLE);
|
||||
myTreatUnknownMembersAsNullable.getModel().addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = myTreatUnknownMembersAsNullable.isSelected();
|
||||
}
|
||||
});
|
||||
|
||||
gc.insets = new Insets(0, 0, 0, 0);
|
||||
gc.gridy = 0;
|
||||
add(mySuggestNullables, gc);
|
||||
@@ -167,6 +177,9 @@ public class DataFlowInspection extends DataFlowInspectionBase {
|
||||
|
||||
gc.gridy++;
|
||||
add(myReportConstantReferences, gc);
|
||||
|
||||
gc.gridy++;
|
||||
add(myTreatUnknownMembersAsNullable, gc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class Test {
|
||||
Object o;
|
||||
|
||||
void field() {
|
||||
<warning descr="Method invocation 'o.hashCode()' may produce 'java.lang.NullPointerException'">o.hashCode()</warning>;
|
||||
}
|
||||
|
||||
void callUnknownMethod() {
|
||||
<warning descr="Method invocation 'unknownObject().hashCode()' may produce 'java.lang.NullPointerException'">unknownObject().hashCode()</warning>;
|
||||
}
|
||||
|
||||
void callNotNullMethod() {
|
||||
knownObject().hashCode();
|
||||
}
|
||||
|
||||
native Object unknownObject();
|
||||
|
||||
@NotNull
|
||||
native Object knownObject();
|
||||
}
|
||||
@@ -137,6 +137,13 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
|
||||
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
|
||||
}
|
||||
|
||||
public void testParanoidMode() {
|
||||
final DataFlowInspection inspection = new DataFlowInspection();
|
||||
inspection.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true;
|
||||
myFixture.enableInspections(inspection);
|
||||
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
|
||||
}
|
||||
|
||||
public void testReportConstantReferences() {
|
||||
doTestReportConstantReferences();
|
||||
myFixture.launchAction(myFixture.findSingleIntention("Replace with 'null'"));
|
||||
|
||||
Reference in New Issue
Block a user