diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index a46d1178503c..22c919caecd0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -94,6 +94,10 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { return null; } + PsiElement parent = codeFragment.getParent(); + if (parent instanceof PsiLambdaExpression && codeFragment instanceof PsiExpression) { + addInstruction(new CheckReturnValueInstruction(codeFragment)); + } myCurrentFlow.setFields(myFields.toArray(new DfaVariableValue[myFields.size()])); addInstruction(new ReturnInstruction(false)); @@ -528,6 +532,15 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { finishElement(statement); } + @Override + public void visitLambdaExpression(PsiLambdaExpression expression) { + startElement(expression); + DfaValue dfaValue = myFactory.createValue(expression); + addInstruction(new PushInstruction(dfaValue, expression)); + addInstruction(new LambdaInstruction(expression)); + finishElement(expression); + } + @Override public void visitReturnStatement(PsiReturnStatement statement) { startElement(statement); @@ -538,7 +551,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { if (method != null) { generateBoxingUnboxingInstructionFor(returnValue, method.getReturnType()); } - addInstruction(new CheckReturnValueInstruction(statement)); + addInstruction(new CheckReturnValueInstruction(returnValue)); } returnCheckingFinally(); 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 c6314f7d9bb1..f56c7ec50d02 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 @@ -479,8 +479,8 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { private static void reportNullableReturns(StandardDataFlowRunner runner, DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set reportedAnchors) { for (PsiElement statement : visitor.getProblems(NullabilityProblem.nullableReturn)) { - final PsiExpression expr = ((PsiReturnStatement)statement).getReturnValue(); - assert expr != null; + assert statement instanceof PsiExpression; + final PsiExpression expr = (PsiExpression)statement; if (!reportedAnchors.add(expr)) continue; if (runner.isInNotNullMethod()) { 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 09c4612b0e6e..6c5c9096fac1 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 @@ -226,6 +226,10 @@ public class DataFlowRunner { } } } + else if (instruction instanceof LambdaInstruction) { + PsiLambdaExpression lambdaExpression = ((LambdaInstruction)instruction).getLambdaExpression(); + registerNestedClosures(instructionState, lambdaExpression); + } else if (instruction instanceof EmptyInstruction) { PsiElement anchor = ((EmptyInstruction)instruction).getAnchor(); if (anchor instanceof PsiDeclarationStatement) { @@ -255,6 +259,14 @@ public class DataFlowRunner { myNestedClosures.putValue(field, createClosureState(state)); } } + + private void registerNestedClosures(DfaInstructionState instructionState, PsiLambdaExpression expr) { + DfaMemoryState state = instructionState.getMemoryState(); + PsiElement body = expr.getBody(); + if (body != null) { + myNestedClosures.putValue(body, createClosureState(state)); + } + } protected ControlFlowAnalyzer createControlFlowAnalyzer() { return new ControlFlowAnalyzer(myValueFactory); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java index b1d3203414a9..0b9f0b010911 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java @@ -54,6 +54,10 @@ public abstract class InstructionVisitor { return nextInstruction(instruction, runner, memState); } + public DfaInstructionState[] visitLambdaExpression(LambdaInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { + return nextInstruction(instruction, runner, memState); + } + public DfaInstructionState[] visitConditionalGoto(ConditionalGotoInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { DfaValue cond = memState.pop(); 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 3ada3e849647..2652c6ff19f6 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 @@ -27,10 +27,7 @@ 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.psi.CommonClassNames; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiType; +import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; import java.util.HashSet; @@ -49,13 +46,23 @@ public class StandardDataFlowRunner extends DataFlowRunner { @Override protected void prepareAnalysis(@NotNull PsiElement psiBlock, Iterable initialStates) { - myIsInMethod = psiBlock.getParent() instanceof PsiMethod; + PsiElement parent = psiBlock.getParent(); + myIsInMethod = parent instanceof PsiMethod; if (myIsInMethod) { - PsiMethod method = (PsiMethod)psiBlock.getParent(); + PsiMethod method = (PsiMethod)parent; PsiType returnType = method.getReturnType(); myInNullableMethod = NullableNotNullManager.isNullable(method) || returnType != null && returnType.equalsToText(CommonClassNames.JAVA_LANG_VOID); myInNotNullMethod = NullableNotNullManager.isNotNull(method); + } else if (parent instanceof PsiLambdaExpression) { + PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(((PsiLambdaExpression)parent).getFunctionalInterfaceType()); + if (method != null) { + myIsInMethod = true; + PsiType returnType = method.getReturnType(); + myInNullableMethod = NullableNotNullManager.isNullable(method) || + returnType != null && returnType.equalsToText(CommonClassNames.JAVA_LANG_VOID); + myInNotNullMethod = NullableNotNullManager.isNotNull(method); + } } myCCEInstructions.clear(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/CheckReturnValueInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/CheckReturnValueInstruction.java index a668a7bdd0e2..6d17463b1318 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/CheckReturnValueInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/CheckReturnValueInstruction.java @@ -19,16 +19,16 @@ import com.intellij.codeInspection.dataFlow.DataFlowRunner; import com.intellij.codeInspection.dataFlow.DfaInstructionState; import com.intellij.codeInspection.dataFlow.DfaMemoryState; import com.intellij.codeInspection.dataFlow.InstructionVisitor; -import com.intellij.psi.PsiReturnStatement; +import com.intellij.psi.PsiElement; /** * @author max */ public class CheckReturnValueInstruction extends Instruction { - private final PsiReturnStatement myReturn; + private final PsiElement myReturnValue; - public CheckReturnValueInstruction(final PsiReturnStatement aReturn) { - myReturn = aReturn; + public CheckReturnValueInstruction(final PsiElement returnValue) { + myReturnValue = returnValue; } @Override @@ -36,8 +36,8 @@ public class CheckReturnValueInstruction extends Instruction { return visitor.visitCheckReturnValue(this, runner, stateBefore); } - public PsiReturnStatement getReturn() { - return myReturn; + public PsiElement getReturn() { + return myReturnValue; } public String toString() { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/LambdaInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/LambdaInstruction.java new file mode 100644 index 000000000000..a0cedb95d948 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/LambdaInstruction.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.dataFlow.instructions; + +import com.intellij.codeInspection.dataFlow.DataFlowRunner; +import com.intellij.codeInspection.dataFlow.DfaInstructionState; +import com.intellij.codeInspection.dataFlow.DfaMemoryState; +import com.intellij.codeInspection.dataFlow.InstructionVisitor; +import com.intellij.psi.PsiLambdaExpression; + +public class LambdaInstruction extends Instruction { + private final PsiLambdaExpression myLambdaExpression; + + public LambdaInstruction(PsiLambdaExpression lambdaExpression) { + myLambdaExpression = lambdaExpression; + } + + public PsiLambdaExpression getLambdaExpression() { + return myLambdaExpression; + } + + @Override + public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) { + return visitor.visitLambdaExpression(this, runner, stateBefore); + } + + @Override + public String toString() { + return "LambdaInstruction"; + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ReturnNullInLambdaExpression.java b/java/java-tests/testData/inspection/dataFlow/fixture/ReturnNullInLambdaExpression.java new file mode 100644 index 000000000000..92d0f422d0bf --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ReturnNullInLambdaExpression.java @@ -0,0 +1,12 @@ +import org.jetbrains.annotations.NotNull; + +class Test { + interface I { + @NotNull + String get(); + } + + { + I i = () -> null; + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ReturnNullInLambdaStatement.java b/java/java-tests/testData/inspection/dataFlow/fixture/ReturnNullInLambdaStatement.java new file mode 100644 index 000000000000..0566fcec540a --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ReturnNullInLambdaStatement.java @@ -0,0 +1,12 @@ +import org.jetbrains.annotations.NotNull; + +class Test { + interface I { + @NotNull + String get(); + } + + { + I i = () -> {return null;}; + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java index 0e7d62016f42..0a7b46aa507a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java @@ -45,6 +45,14 @@ public class DataFlowInspection8Test extends LightCodeInsightFixtureTestCase { doTest(); } + public void testReturnNullInLambdaExpression() throws Exception { + doTest(); + } + + public void testReturnNullInLambdaStatement() throws Exception { + doTest(); + } + private void setupCustomAnnotations() { myFixture.addClass("package foo;\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE_USE}) public @interface Nullable { }"); myFixture.addClass("package foo;\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE_USE}) public @interface NotNull { }");