ControlFlowAnalyzer: store lambda return val to variable when inlining block

Should fix EA-107095 - CCE: ControlTransferInstruction.accept
This commit is contained in:
Tagir Valeev
2017-09-18 14:45:40 +07:00
parent 179016ef94
commit df95dbba8f
3 changed files with 103 additions and 48 deletions
@@ -568,8 +568,33 @@ public class CFGBuilder {
}
}
/**
* Inlines given lambda. Lambda parameters are assumed to be assigned already (if necessary).
* <p>
* Stack before: ...
* <p>
* 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;
}
@@ -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;
}
}
@@ -27,4 +27,20 @@ public class LambdaInlining {
System.out.println("oops");
}
}
void testLambdaTryFinally() {
int x = ((IntSupplier)() -> {
try {
return 10;
} finally {
return 20;
}
}).getAsInt();
if(<warning descr="Condition 'x == 30' is always 'false'">x == 30</warning>) {
System.out.println("oops");
}
if(<warning descr="Condition 'x < 30' is always 'true'">x < 30</warning>) {
System.out.println("always");
}
}
}