DFA: separate warning to store into notnull array; fixes for assignment

Review ID: IDEA-CR-24027
This commit is contained in:
Tagir Valeev
2017-09-06 11:48:40 +07:00
parent b6bc6c4275
commit 2fc090d5cc
7 changed files with 48 additions and 10 deletions
@@ -1064,7 +1064,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
if (componentType != null) {
generateBoxingUnboxingInstructionFor(initializer, componentType);
if (componentNullability == Nullness.NOT_NULL) {
addInstruction(new CheckNotNullInstruction(initializer, NullabilityProblem.assigningToNotNull));
addInstruction(new CheckNotNullInstruction(initializer, NullabilityProblem.storingToNotNullArray));
}
}
addInstruction(new PopInstruction());
@@ -325,7 +325,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
reportNullableFunctions(visitor, holder, reportedAnchors);
reportNullableArguments(visitor, holder, reportedAnchors);
reportNullableAssignments(visitor, holder, reportedAnchors);
reportNullableAssignments(visitor, holder, reportedAnchors, onTheFly);
reportUnboxedNullables(visitor, holder, reportedAnchors);
reportNullableReturns(visitor, holder, reportedAnchors, scope);
if (SUGGEST_NULLABLE_ANNOTATIONS) {
@@ -682,14 +682,29 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
}
private static void reportNullableAssignments(DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
private void reportNullableAssignments(DataFlowInstructionVisitor visitor,
ProblemsHolder holder,
Set<PsiElement> reportedAnchors,
boolean onTheFly) {
for (PsiElement expr : visitor.getProblems(NullabilityProblem.assigningToNotNull)) {
if (!reportedAnchors.add(expr)) continue;
assert expr instanceof PsiExpression;
final String text = isNullLiteralExpression(expr)
? InspectionsBundle.message("dataflow.message.assigning.null")
: InspectionsBundle.message("dataflow.message.assigning.nullable");
holder.registerProblem(expr, text);
holder.registerProblem(expr, text,
createNPEFixes((PsiExpression)expr, (PsiExpression)expr, onTheFly).toArray(LocalQuickFix.EMPTY_ARRAY));
}
for (PsiElement expr : visitor.getProblems(NullabilityProblem.storingToNotNullArray)) {
if (!reportedAnchors.add(expr)) continue;
assert expr instanceof PsiExpression;
final String text = isNullLiteralExpression(expr)
? InspectionsBundle.message("dataflow.message.storing.array.null")
: InspectionsBundle.message("dataflow.message.storing.array.nullable");
holder.registerProblem(expr, text,
createNPEFixes((PsiExpression)expr, (PsiExpression)expr, onTheFly).toArray(LocalQuickFix.EMPTY_ARRAY));
}
}
@@ -8,6 +8,7 @@ public enum NullabilityProblem {
fieldAccessNPE,
unboxingNullable,
assigningToNotNull,
storingToNotNullArray,
nullableReturn,
nullableFunctionReturn,
passingNullableToNotNullParameter,
@@ -72,13 +72,16 @@ public class StandardInstructionVisitor extends InstructionVisitor {
dfaDest = instruction.getAssignedValue();
}
NullabilityProblem problem = PsiUtil.skipParenthesizedExprDown(instruction.getLExpression()) instanceof PsiArrayAccessExpression ?
NullabilityProblem.storingToNotNullArray : NullabilityProblem.assigningToNotNull;
if (dfaDest instanceof DfaVariableValue) {
DfaVariableValue var = (DfaVariableValue) dfaDest;
final PsiModifierListOwner psi = var.getPsiVariable();
boolean forceDeclaredNullity = !(psi instanceof PsiParameter && psi.getParent() instanceof PsiParameterList);
if (forceDeclaredNullity && var.getInherentNullability() == Nullness.NOT_NULL) {
checkNotNullable(memState, dfaSource, NullabilityProblem.assigningToNotNull, instruction.getRExpression());
checkNotNullable(memState, dfaSource, problem, instruction.getRExpression());
}
if (!(psi instanceof PsiField) || !psi.hasModifierProperty(PsiModifier.VOLATILE)) {
memState.setVarValue(var, dfaSource);
@@ -89,7 +92,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
} else if (dfaDest instanceof DfaTypeValue && ((DfaTypeValue)dfaDest).isNotNull()) {
checkNotNullable(memState, dfaSource, NullabilityProblem.assigningToNotNull, instruction.getRExpression());
checkNotNullable(memState, dfaSource, problem, instruction.getRExpression());
}
memState.push(dfaDest);
@@ -21,6 +21,7 @@ import com.intellij.codeInspection.dataFlow.DfaInstructionState;
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.psi.PsiAssignmentExpression;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiVariable;
import org.jetbrains.annotations.Nullable;
@@ -44,6 +45,15 @@ public class AssignInstruction extends Instruction {
return myRExpression;
}
@Nullable
public PsiExpression getLExpression() {
if(myRExpression == null) return null;
if(myRExpression.getParent() instanceof PsiAssignmentExpression) {
return ((PsiAssignmentExpression)myRExpression.getParent()).getLExpression();
}
return null;
}
public boolean isVariableInitializer() {
return myRExpression != null && myRExpression.getParent() instanceof PsiVariable;
}
@@ -19,10 +19,17 @@ class FooImpl implements Foo {
return null;
}
else if (Math.random() > 0.5) {
@NotNull Object @Nullable [] arr1 = new Object[]{<warning descr="'null' is assigned to a variable that is annotated with @NotNull">null</warning>, new Object(), <warning descr="Expression 'Math.random() > 0.5 ? new Object() : null' might evaluate to null but is assigned to a variable that is annotated with @NotNull">Math.random() > 0.5 ? new Object() : null</warning>};
@NotNull Object @Nullable [] arr2 = {<warning descr="'null' is assigned to a variable that is annotated with @NotNull">null</warning>, new Object(), <warning descr="Expression 'Math.random() > 0.5 ? new Object() : null' might evaluate to null but is assigned to a variable that is annotated with @NotNull">Math.random() > 0.5 ? new Object() : null</warning>};
return new Object[]{<warning descr="'null' is assigned to a variable that is annotated with @NotNull">null</warning>, new Object(), <warning descr="Expression 'Math.random() > 0.5 ? new Object() : null' might evaluate to null but is assigned to a variable that is annotated with @NotNull">Math.random() > 0.5 ? new Object() : null</warning>};
@NotNull Object @Nullable [] arr1 = new Object[]{<warning descr="'null' is stored to an array which elements are @NotNull">null</warning>, new Object(), <warning descr="Expression 'Math.random() > 0.5 ? new Object() : null' might evaluate to null but is stored to an array of @NotNull elements">Math.random() > 0.5 ? new Object() : null</warning>};
@NotNull Object @Nullable [] arr2 = {<warning descr="'null' is stored to an array which elements are @NotNull">null</warning>, new Object(), <warning descr="Expression 'Math.random() > 0.5 ? new Object() : null' might evaluate to null but is stored to an array of @NotNull elements">Math.random() > 0.5 ? new Object() : null</warning>};
return new Object[]{<warning descr="'null' is stored to an array which elements are @NotNull">null</warning>, new Object(), <warning descr="Expression 'Math.random() > 0.5 ? new Object() : null' might evaluate to null but is stored to an array of @NotNull elements">Math.random() > 0.5 ? new Object() : null</warning>};
}
return new @NotNull Object @Nullable []{<warning descr="'null' is assigned to a variable that is annotated with @NotNull">null</warning>, new Object(), <warning descr="Expression 'Math.random() > 0.5 ? new Object() : null' might evaluate to null but is assigned to a variable that is annotated with @NotNull">Math.random() > 0.5 ? new Object() : null</warning>};
return new @NotNull Object @Nullable []{<warning descr="'null' is stored to an array which elements are @NotNull">null</warning>, new Object(), <warning descr="Expression 'Math.random() > 0.5 ? new Object() : null' might evaluate to null but is stored to an array of @NotNull elements">Math.random() > 0.5 ? new Object() : null</warning>};
}
void test() {
@NotNull Object @Nullable [] array = getNullableArrayOfNotNullObjects();
assert array != null;
array[0] = <warning descr="'null' is stored to an array which elements are @NotNull">null</warning>;
array[1] = <warning descr="Expression 'Math.random() > 0.5 ? null : \"foo\"' might evaluate to null but is stored to an array of @NotNull elements">Math.random() > 0.5 ? null : "foo"</warning>;
}
}
@@ -85,6 +85,8 @@ dataflow.message.passing.nullable.argument=Argument <code>#ref</code> #loc might
dataflow.message.passing.nullable.argument.methodref=Method reference argument might be null
dataflow.message.assigning.null=<code>null</code> is assigned to a variable that is annotated with @NotNull
dataflow.message.assigning.nullable=Expression <code>#ref</code> might evaluate to null but is assigned to a variable that is annotated with @NotNull
dataflow.message.storing.array.null=<code>null</code> is stored to an array which elements are @NotNull
dataflow.message.storing.array.nullable=Expression <code>#ref</code> might evaluate to null but is stored to an array of @NotNull elements
dataflow.message.return.null.from.notnull=<code>null</code> is returned by the method declared as @{0}
dataflow.message.return.nullable.from.notnull=Expression <code>#ref</code> might evaluate to null but is returned by the method declared as @{0}
dataflow.message.return.null.from.notnullable=<code>null</code> is returned by the method which is not declared as @{0}