ControlFlow: properly throw exceptions of methods with side-effects

Fixes IDEA-215334 Dataflow doesn't see the array write inside lambda in try section

GitOrigin-RevId: e6b89b6b3a25cb3b0ace290455be11fbe261a0d2
This commit is contained in:
Tagir Valeev
2019-06-03 12:08:03 +03:00
committed by intellij-monorepo-bot
parent bc87963baa
commit e414dc7502
5 changed files with 35 additions and 16 deletions
@@ -689,7 +689,7 @@ public class CFGBuilder {
}
// Unknown function
flushFields();
myAnalyzer.addConditionalRuntimeThrow();
myAnalyzer.addConditionalErrorThrow();
PsiType functionalInterfaceType = functionalExpression.getType();
myAnalyzer.addMethodThrows(LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType), null);
PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
@@ -129,6 +129,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
return myCodeFragment;
}
@NotNull
private PsiClassType createClassType(GlobalSearchScope scope, String fqn) {
PsiClass aClass = JavaPsiFacade.getInstance(myProject).findClass(fqn, scope);
if (aClass != null) return JavaPsiFacade.getElementFactory(myProject).createType(aClass);
@@ -1028,7 +1029,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
finishElement(statement);
}
void addConditionalRuntimeThrow() {
void addConditionalErrorThrow() {
if (!shouldHandleException()) {
return;
}
@@ -1036,10 +1037,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
pushUnknown();
final ConditionalGotoInstruction ifNoException = addInstruction(new ConditionalGotoInstruction(null, false, null));
pushUnknown();
final ConditionalGotoInstruction ifError = addInstruction(new ConditionalGotoInstruction(null, false, null));
throwException(myExceptionCache.get(JAVA_LANG_RUNTIME_EXCEPTION), null);
ifError.setOffset(myCurrentFlow.getInstructionCount());
throwException(myExceptionCache.get(JAVA_LANG_ERROR), null);
ifNoException.setOffset(myCurrentFlow.getInstructionCount());
@@ -1145,7 +1142,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
pushTrap(new InsideFinally(resourceList));
startElement(resourceList);
addInstruction(new FlushFieldsInstruction());
addThrows(null, closerExceptions.toArray(PsiClassType.EMPTY_ARRAY));
addThrows(null, closerExceptions);
controlTransfer(new ExitFinallyTransfer(twrFinallyDescriptor), FList.emptyList()); // DfaControlTransferValue is on stack
finishElement(resourceList);
popTrap(InsideFinally.class);
@@ -1489,7 +1486,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
addInstruction(new UnwrapSpecialFieldInstruction(SpecialField.UNBOX));
}
else if (TypeConversionUtil.isPrimitiveAndNotNull(actualType) && TypeConversionUtil.isAssignableFromPrimitiveWrapper(expectedType)) {
addConditionalRuntimeThrow();
addConditionalErrorThrow();
PsiType boxedType = ((PsiPrimitiveType)actualType).getBoxedType(context);
addInstruction(new BoxingInstruction(boxedType));
}
@@ -1648,13 +1645,17 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
}
void addMethodThrows(PsiMethod method, @Nullable PsiElement explicitCall) {
if (method != null && shouldHandleException()) {
addThrows(explicitCall, method.getThrowsList().getReferencedTypes());
if (shouldHandleException()) {
addThrows(explicitCall, method == null ? Collections.emptyList() : Arrays.asList(method.getThrowsList().getReferencedTypes()));
}
}
private void addThrows(@Nullable PsiElement explicitCall, PsiClassType[] refs) {
for (PsiClassType ref : refs) {
private void addThrows(@Nullable PsiElement explicitCall, Collection<? extends PsiType> exceptions) {
List<PsiType> allExceptions = new ArrayList<>(exceptions);
allExceptions.add(myExceptionCache.get(JAVA_LANG_ERROR).getThrowable().getPsiType());
allExceptions.add(myExceptionCache.get(JAVA_LANG_RUNTIME_EXCEPTION).getThrowable().getPsiType());
List<PsiType> refs = PsiDisjunctionType.flattenAndRemoveDuplicates(allExceptions);
for (PsiType ref : refs) {
pushUnknown();
ConditionalGotoInstruction cond = new ConditionalGotoInstruction(null, false, null);
addInstruction(cond);
@@ -1743,7 +1744,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
}
void addBareCall(@Nullable PsiMethodCallExpression expression, @NotNull PsiReferenceExpression reference) {
addConditionalRuntimeThrow();
addConditionalErrorThrow();
PsiMethod method = ObjectUtils.tryCast(reference.resolve(), PsiMethod.class);
List<? extends MethodContract> contracts =
method == null ? Collections.emptyList() :
@@ -1766,7 +1767,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, PsiType.BOOLEAN));
ConditionalGotoInstruction ifNotFail = new ConditionalGotoInstruction(null, true, null);
addInstruction(ifNotFail);
addInstruction(new ReturnInstruction(myFactory.controlTransfer(new ExceptionTransfer(null), myTrapStack), anchor));
addInstruction(new ReturnInstruction(myFactory.controlTransfer(myExceptionCache.get(JAVA_LANG_THROWABLE), myTrapStack), anchor));
ifNotFail.setOffset(myCurrentFlow.getInstructionCount());
}
@@ -1852,7 +1853,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
handleEscapedVariables(anonymousClass);
}
addConditionalRuntimeThrow();
addConditionalErrorThrow();
DfaValue precalculatedNewValue = getPrecalculatedNewValue(expression);
List<? extends MethodContract> contracts = constructor == null ? Collections.emptyList() : JavaMethodContractUtil.getMethodContracts(constructor);
addInstruction(new MethodCallInstruction(expression, precalculatedNewValue, DfaUtil.addRangeContracts(constructor, contracts)));
@@ -43,7 +43,7 @@ interface TransferTarget {
/** @return next instruction states assuming no traps */
fun dispatch(state: DfaMemoryState, runner: DataFlowRunner) : List<DfaInstructionState> = emptyList()
}
data class ExceptionTransfer(val throwable: DfaPsiType?) : TransferTarget {
data class ExceptionTransfer(val throwable: DfaPsiType) : TransferTarget {
override fun toString(): String = "Exception($throwable)"
}
data class InstructionTransfer(val offset: ControlFlow.ControlFlowOffset, private val toFlush: List<DfaVariableValue>) : TransferTarget {
@@ -0,0 +1,17 @@
class App {
static void f(Runnable r) {
r.run();
}
public static void main(String[] args) {
RuntimeException[] exception = {null};
try {
f(() -> {
exception[0] = new RuntimeException();
throw exception[0];
});
} catch (RuntimeException e) {
System.out.println(e == exception[0]); // was false-positive
}
}
}
@@ -245,4 +245,5 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
doTest();
}
public void testLambdaWritesArrayInTry() { doTest(); }
}