diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index 8c3804d8a500..40419427e048 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -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 nestedClosures = dfaRunner.getNestedClosures(); for (PsiElement closure : nestedClosures.keySet()) { @@ -226,7 +227,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { protected void addSurroundWithIfFix(PsiExpression qualifier, List 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> constConditions = runner.getConstConditionalExpressions(); Set trueSet = constConditions.getFirst(); Set 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 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 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"); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java index a9b0c5b2d70a..c42d670e0be1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java @@ -67,9 +67,6 @@ public class DataFlowRunner { return myValueFactory; } - protected void prepareAnalysis(@NotNull PsiElement psiBlock, Iterable initialStates) { - } - @Nullable private Collection createInitialStates(@NotNull PsiElement psiBlock, InstructionVisitor visitor) { PsiClass containingClass = PsiTreeUtil.getParentOfType(psiBlock, PsiClass.class); @@ -101,8 +98,6 @@ public class DataFlowRunner { boolean ignoreAssertions, @NotNull Collection initialStates) { try { - prepareAnalysis(psiBlock, initialStates); - final ControlFlow flow = createControlFlowAnalyzer().buildControlFlow(psiBlock, ignoreAssertions); if (flow == null) return RunnerResult.NOT_APPLICABLE; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java index 6ca85c46d20f..987054ad360a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java @@ -126,13 +126,6 @@ public class DfaPsiUtil { final PsiCodeBlock body = constructor.getBody(); final Map map = ContainerUtil.newHashMap(); final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(false, false) { - boolean shouldCheck; - - @Override - protected void prepareAnalysis(@NotNull PsiElement psiBlock, Iterable 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); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java index 0d951c81f11d..e0c2034c0c2d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java @@ -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 myCCEInstructions = new HashSet(); - 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 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 getRedundantInstanceofs(final DataFlowRunner runner, StandardInstructionVisitor visitor) { HashSet result = new HashSet(1); for (Instruction instruction : runner.getInstructions()) {