mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
committed by
intellij-monorepo-bot
parent
bc87963baa
commit
e414dc7502
@@ -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);
|
||||
|
||||
+15
-14
@@ -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(); }
|
||||
}
|
||||
Reference in New Issue
Block a user