dfa: remove unnecessary statefulness and clarify void type checks

This commit is contained in:
peter
2015-05-27 09:44:49 +02:00
parent 27fa935da3
commit 3ddd7d82df
4 changed files with 35 additions and 78 deletions
@@ -34,6 +34,7 @@ import com.intellij.codeInspection.*;
import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.nullable.NullableStuffInspectionBase;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
@@ -168,7 +169,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
final DataFlowInstructionVisitor visitor = new DataFlowInstructionVisitor(dfaRunner);
final RunnerResult rc = dfaRunner.analyzeMethod(scope, visitor, IGNORE_ASSERT_STATEMENTS, initialStates);
if (rc == RunnerResult.OK) {
createDescription(dfaRunner, holder, visitor, onTheFly);
createDescription(dfaRunner, holder, visitor, onTheFly, scope);
MultiMap<PsiElement,DfaMemoryState> nestedClosures = dfaRunner.getNestedClosures();
for (PsiElement closure : nestedClosures.keySet()) {
@@ -226,7 +227,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
protected void addSurroundWithIfFix(PsiExpression qualifier, List<LocalQuickFix> fixes, boolean onTheFly) {
}
private void createDescription(StandardDataFlowRunner runner, ProblemsHolder holder, DataFlowInstructionVisitor visitor, final boolean onTheFly) {
private void createDescription(StandardDataFlowRunner runner, ProblemsHolder holder, DataFlowInstructionVisitor visitor, final boolean onTheFly, PsiElement scope) {
Pair<Set<Instruction>, Set<Instruction>> constConditions = runner.getConstConditionalExpressions();
Set<Instruction> trueSet = constConditions.getFirst();
Set<Instruction> falseSet = constConditions.getSecond();
@@ -264,9 +265,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
reportNullableArguments(visitor, holder, reportedAnchors);
reportNullableAssignments(visitor, holder, reportedAnchors);
reportUnboxedNullables(visitor, holder, reportedAnchors);
if (!runner.isInNullableMethod() && runner.isInMethod() && (runner.isInNotNullMethod() || SUGGEST_NULLABLE_ANNOTATIONS)) {
reportNullableReturns(runner, visitor, holder, reportedAnchors);
}
reportNullableReturns(visitor, holder, reportedAnchors, getScopeMethod(scope));
if (SUGGEST_NULLABLE_ANNOTATIONS) {
reportNullableArgumentsPassedToNonAnnotated(visitor, holder, reportedAnchors);
}
@@ -497,13 +496,33 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
}
private static void reportNullableReturns(StandardDataFlowRunner runner, DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
@Nullable
private static PsiMethod getScopeMethod(PsiElement block) {
PsiElement parent = block.getParent();
if (parent instanceof PsiMethod) return (PsiMethod)parent;
if (parent instanceof PsiLambdaExpression) return LambdaUtil.getFunctionalInterfaceMethod(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
return null;
}
private void reportNullableReturns(DataFlowInstructionVisitor visitor,
ProblemsHolder holder,
Set<PsiElement> reportedAnchors,
@Nullable PsiMethod method) {
if (method == null || NullableStuffInspectionBase.isNullableNotInferred(method, true)) return;
boolean notNullRequired = NullableNotNullManager.isNotNull(method);
if (!notNullRequired && !SUGGEST_NULLABLE_ANNOTATIONS) return;
PsiType returnType = method.getReturnType();
// no warnings for Void methods (where only null can be possibly returned), or in void lambdas, where the last expression is not returned anyway
if (returnType == null || returnType == PsiType.VOID || returnType.equalsToText(CommonClassNames.JAVA_LANG_VOID)) return;
for (PsiElement statement : visitor.getProblems(NullabilityProblem.nullableReturn)) {
assert statement instanceof PsiExpression;
final PsiExpression expr = (PsiExpression)statement;
if (!reportedAnchors.add(expr)) continue;
if (runner.isInNotNullMethod()) {
if (notNullRequired) {
final String text = isNullLiteralExpression(expr)
? InspectionsBundle.message("dataflow.message.return.null.from.notnull")
: InspectionsBundle.message("dataflow.message.return.nullable.from.notnull");
@@ -67,9 +67,6 @@ public class DataFlowRunner {
return myValueFactory;
}
protected void prepareAnalysis(@NotNull PsiElement psiBlock, Iterable<DfaMemoryState> initialStates) {
}
@Nullable
private Collection<DfaMemoryState> createInitialStates(@NotNull PsiElement psiBlock, InstructionVisitor visitor) {
PsiClass containingClass = PsiTreeUtil.getParentOfType(psiBlock, PsiClass.class);
@@ -101,8 +98,6 @@ public class DataFlowRunner {
boolean ignoreAssertions,
@NotNull Collection<DfaMemoryState> initialStates) {
try {
prepareAnalysis(psiBlock, initialStates);
final ControlFlow flow = createControlFlowAnalyzer().buildControlFlow(psiBlock, ignoreAssertions);
if (flow == null) return RunnerResult.NOT_APPLICABLE;
@@ -126,13 +126,6 @@ public class DfaPsiUtil {
final PsiCodeBlock body = constructor.getBody();
final Map<PsiField, Boolean> map = ContainerUtil.newHashMap();
final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(false, false) {
boolean shouldCheck;
@Override
protected void prepareAnalysis(@NotNull PsiElement psiBlock, Iterable<DfaMemoryState> initialStates) {
super.prepareAnalysis(psiBlock, initialStates);
shouldCheck = psiBlock == body;
}
private boolean isCallExposingNonInitializedFields(Instruction instruction) {
if (!(instruction instanceof MethodCallInstruction) ||
@@ -160,19 +153,17 @@ public class DfaPsiUtil {
@Override
protected DfaInstructionState[] acceptInstruction(InstructionVisitor visitor, DfaInstructionState instructionState) {
if (shouldCheck) {
Instruction instruction = instructionState.getInstruction();
if (isCallExposingNonInitializedFields(instruction) ||
instruction instanceof ReturnInstruction && !((ReturnInstruction)instruction).isViaException()) {
for (PsiField field : containingClass.getFields()) {
if (!instructionState.getMemoryState().isNotNull(getFactory().getVarFactory().createVariableValue(field, false))) {
map.put(field, false);
} else if (!map.containsKey(field)) {
map.put(field, true);
}
Instruction instruction = instructionState.getInstruction();
if (isCallExposingNonInitializedFields(instruction) ||
instruction instanceof ReturnInstruction && !((ReturnInstruction)instruction).isViaException()) {
for (PsiField field : containingClass.getFields()) {
if (!instructionState.getMemoryState().isNotNull(getFactory().getVarFactory().createVariableValue(field, false))) {
map.put(field, false);
} else if (!map.containsKey(field)) {
map.put(field, true);
}
return DfaInstructionState.EMPTY_ARRAY;
}
return DfaInstructionState.EMPTY_ARRAY;
}
return super.acceptInstruction(visitor, instructionState);
}
@@ -24,11 +24,8 @@
*/
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.instructions.InstanceofInstruction;
import com.intellij.codeInspection.dataFlow.instructions.Instruction;
import com.intellij.codeInspection.nullable.NullableStuffInspectionBase;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
@@ -37,10 +34,6 @@ import java.util.Set;
public class StandardDataFlowRunner extends DataFlowRunner {
private final Set<Instruction> myCCEInstructions = new HashSet<Instruction>();
private boolean myInNullableMethod = false;
private boolean myInNotNullMethod = false;
private boolean myIsInMethod = false;
public StandardDataFlowRunner() {
this(false, true);
}
@@ -48,35 +41,6 @@ public class StandardDataFlowRunner extends DataFlowRunner {
super(unknownMembersAreNullable, honorFieldInitializers);
}
@Override
protected void prepareAnalysis(@NotNull PsiElement psiBlock, Iterable<DfaMemoryState> initialStates) {
PsiElement parent = psiBlock.getParent();
myIsInMethod = parent instanceof PsiMethod;
if (myIsInMethod) {
PsiMethod method = (PsiMethod)parent;
myInNullableMethod = isTreatedAsNullable(method);
myInNotNullMethod = NullableNotNullManager.isNotNull(method);
} else if (parent instanceof PsiLambdaExpression) {
PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
if (method != null) {
myIsInMethod = true;
myInNullableMethod = isTreatedAsNullable(method);
myInNotNullMethod = NullableNotNullManager.isNotNull(method);
}
}
myCCEInstructions.clear();
}
private static boolean isTreatedAsNullable(PsiMethod method) {
if (NullableStuffInspectionBase.isNullableNotInferred(method, true)) {
return true;
}
PsiType returnType = method.getReturnType();
return returnType != null && (returnType == PsiType.VOID || returnType.equalsToText(CommonClassNames.JAVA_LANG_VOID));
}
public void onInstructionProducesCCE(Instruction instruction) {
myCCEInstructions.add(instruction);
}
@@ -85,18 +49,6 @@ public class StandardDataFlowRunner extends DataFlowRunner {
return myCCEInstructions;
}
public boolean isInNotNullMethod() {
return myInNotNullMethod;
}
public boolean isInNullableMethod() {
return myInNullableMethod;
}
public boolean isInMethod() {
return myIsInMethod;
}
@NotNull public static Set<Instruction> getRedundantInstanceofs(final DataFlowRunner runner, StandardInstructionVisitor visitor) {
HashSet<Instruction> result = new HashSet<Instruction>(1);
for (Instruction instruction : runner.getInstructions()) {