diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java index 6ff54eb7b78b..e39753cd7866 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java @@ -568,8 +568,33 @@ public class CFGBuilder { } } + /** + * Inlines given lambda. Lambda parameters are assumed to be assigned already (if necessary). + *

+ * Stack before: ... + *

+ * Stack after: ... lambdaResult + * + * @param lambda lambda to inline + * @param resultNullness a required return value nullness + * @return this builder + */ public CFGBuilder inlineLambda(PsiLambdaExpression lambda, Nullness resultNullness) { - myAnalyzer.inlineLambda(lambda, resultNullness); + PsiElement body = lambda.getBody(); + PsiExpression expression = LambdaUtil.extractSingleExpressionFromBody(body); + if (expression != null) { + pushExpression(expression); + boxUnbox(expression, LambdaUtil.getFunctionalInterfaceReturnType(lambda)); + if(resultNullness == Nullness.NOT_NULL) { + checkNotNull(expression, NullabilityProblem.nullableFunctionReturn); + } + } else if(body instanceof PsiCodeBlock) { + PsiVariable variable = createTempVariable(LambdaUtil.getFunctionalInterfaceReturnType(lambda)); + myAnalyzer.inlineBlock((PsiCodeBlock)body, resultNullness, variable); + push(getFactory().getVarFactory().createVariableValue(variable, false)); + } else { + pushUnknown(); + } return this; } 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 288a39f330f7..276a0482e5a7 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 @@ -63,8 +63,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { private final ExceptionTransfer myRuntimeException; private final ExceptionTransfer myError; private final PsiType myAssertionError; - private PsiLambdaExpression myLambdaExpression = null; - private boolean myForceNotNullLambdaResult = false; + private InlinedBlockContext myInlinedBlockContext; ControlFlowAnalyzer(final DfaValueFactory valueFactory, @NotNull PsiElement codeFragment, boolean ignoreAssertions, boolean inlining) { myInlining = inlining; @@ -685,34 +684,42 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { startElement(statement); PsiExpression returnValue = statement.getReturnValue(); - if (returnValue != null) { - returnValue.accept(this); - PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, true, PsiMember.class, PsiLambdaExpression.class); - if (method != null) { - generateBoxingUnboxingInstructionFor(returnValue, method.getReturnType()); - } - else { - final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(statement, PsiLambdaExpression.class, true, PsiMember.class); - if (lambdaExpression != null) { - generateBoxingUnboxingInstructionFor(returnValue, LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression)); - } - } - } - if (myLambdaExpression == null) { + if (myInlinedBlockContext != null) { if (returnValue != null) { + DfaVariableValue var = myFactory.getVarFactory().createVariableValue(myInlinedBlockContext.myTarget, false); + addInstruction(new PushInstruction(var, null, true)); + returnValue.accept(this); + generateBoxingUnboxingInstructionFor(returnValue, var.getVariableType()); + if (myInlinedBlockContext.myForceNonNullBlockResult) { + addInstruction(new CheckNotNullInstruction(returnValue, NullabilityProblem.nullableFunctionReturn)); + } + addInstruction(new AssignInstruction(returnValue, null)); + addInstruction(new PopInstruction()); + } + + controlTransfer(new InstructionTransfer(getEndOffset(myInlinedBlockContext.myCodeBlock), getVariablesInside( + myInlinedBlockContext.myCodeBlock)), myTrapStack); + } else { + + if (returnValue != null) { + returnValue.accept(this); + PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, true, PsiMember.class, PsiLambdaExpression.class); + if (method != null) { + generateBoxingUnboxingInstructionFor(returnValue, method.getReturnType()); + } + else { + final PsiLambdaExpression lambdaExpression = + PsiTreeUtil.getParentOfType(statement, PsiLambdaExpression.class, true, PsiMember.class); + if (lambdaExpression != null) { + generateBoxingUnboxingInstructionFor(returnValue, LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression)); + } + } addInstruction(new CheckReturnValueInstruction(returnValue)); } + addInstruction(new ReturnInstruction(myFactory.controlTransfer(ReturnTransfer.INSTANCE, myTrapStack), statement)); } - else { - if (returnValue == null) { - pushUnknown(); - } else if (myForceNotNullLambdaResult) { - addInstruction(new CheckNotNullInstruction(returnValue, NullabilityProblem.nullableFunctionReturn)); - } - controlTransfer(new InstructionTransfer(getEndOffset(myLambdaExpression), getVariablesInside(myLambdaExpression)), myTrapStack); - } finishElement(statement); } @@ -1765,34 +1772,41 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { @Override public void visitClass(PsiClass aClass) { } - void inlineLambda(PsiLambdaExpression lambda, Nullness resultNullness) { - PsiLambdaExpression oldLambda = myLambdaExpression; - boolean oldForceNotNullLambdaResult = myForceNotNullLambdaResult; + /** + * Inline code block (lambda or method body) into this CFG. Incoming parameters are assumed to be handled already (if necessary) + * + * @param block block to inline + * @param resultNullness desired nullness returned by block return statement + * @param target a variable to store the block result (returned via {@code return} statement) + */ + void inlineBlock(@NotNull PsiCodeBlock block, @NotNull Nullness resultNullness, @NotNull PsiVariable target) { + InlinedBlockContext oldBlock = myInlinedBlockContext; // Transfer value is pushed to avoid emptying stack beyond this point addInstruction(new PushInstruction(myFactory.controlTransfer(ReturnTransfer.INSTANCE, this.myTrapStack), null)); - myLambdaExpression = lambda; - myForceNotNullLambdaResult = resultNullness == Nullness.NOT_NULL; - startElement(lambda); + myInlinedBlockContext = new InlinedBlockContext(block, resultNullness == Nullness.NOT_NULL, target); + startElement(block); try { - PsiElement body = lambda.getBody(); - Objects.requireNonNull(body).accept(this); - if (body instanceof PsiCodeBlock) { - // return value for void or incomplete lambda - pushUnknown(); - } - else if (body instanceof PsiExpression) { - generateBoxingUnboxingInstructionFor((PsiExpression)body, LambdaUtil.getFunctionalInterfaceReturnType(lambda)); - if (myForceNotNullLambdaResult) { - addInstruction(new CheckNotNullInstruction((PsiExpression)body, NullabilityProblem.nullableFunctionReturn)); - } - } + block.accept(this); + // return value for void or incomplete block + pushUnknown(); } finally { - finishElement(lambda); - myLambdaExpression = oldLambda; - myForceNotNullLambdaResult = oldForceNotNullLambdaResult; - // Pop transfer value (which is second value in stack now) - addInstruction(new SpliceInstruction(2, 0)); + finishElement(block); + myInlinedBlockContext = oldBlock; + // Pop transfer value + addInstruction(new PopInstruction()); + } + } + + public static class InlinedBlockContext { + final PsiCodeBlock myCodeBlock; + final boolean myForceNonNullBlockResult; + final PsiVariable myTarget; + + public InlinedBlockContext(PsiCodeBlock codeBlock, boolean forceNonNullBlockResult, PsiVariable target) { + myCodeBlock = codeBlock; + myForceNonNullBlockResult = forceNonNullBlockResult; + myTarget = target; } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/LambdaInlining.java b/java/java-tests/testData/inspection/dataFlow/fixture/LambdaInlining.java index 4bc09bca8205..aff0995ad755 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/LambdaInlining.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/LambdaInlining.java @@ -27,4 +27,20 @@ public class LambdaInlining { System.out.println("oops"); } } + + void testLambdaTryFinally() { + int x = ((IntSupplier)() -> { + try { + return 10; + } finally { + return 20; + } + }).getAsInt(); + if(x == 30) { + System.out.println("oops"); + } + if(x < 30) { + System.out.println("always"); + } + } } \ No newline at end of file