boolean (@NotNull final ControlFlow flow, final int flowStart, final int flowEnd, PsiElement @NotNull [] elements, @NotNull PsiElement enclosingCodeFragment) { final List<Instruction> instructions = flow.getInstructions(); class Worker { @NotNull private Map<PsiVariable, IntList> getWritesOffsets() { final Map<PsiVariable, IntList> writeOffsets = new HashMap<>(); for (int i = flowStart; i < flowEnd; i++) { Instruction instruction = instructions.get(i); if (instruction instanceof WriteVariableInstruction) { final PsiVariable variable = ((WriteVariableInstruction)instruction).variable; if (variable instanceof PsiLocalVariable || variable instanceof PsiParameter) { IntList offsets = writeOffsets.get(variable); if (offsets == null) writeOffsets.put(variable, offsets = new IntArrayList()); offsets.add(i); } } } LOG.debug("writeOffsets:", writeOffsets); return writeOffsets; } @NotNull private Map<PsiVariable, IntList> getVisibleReadsOffsets(@NotNull Map<PsiVariable, IntList> writeOffsets, @NotNull PsiCodeBlock tryBlock) { final Map<PsiVariable, IntList> visibleReadOffsets = new HashMap<>(); for (PsiVariable variable : writeOffsets.keySet()) { if (!PsiTreeUtil.isAncestor(tryBlock, variable, true)) { visibleReadOffsets.put(variable, new IntArrayList()); } } if (visibleReadOffsets.isEmpty()) return visibleReadOffsets; for (int i = 0; i < instructions.size(); i++) { final Instruction instruction = instructions.get(i); if (instruction instanceof ReadVariableInstruction) { final PsiVariable variable = ((ReadVariableInstruction)instruction).variable; final IntList readOffsets = visibleReadOffsets.get(variable); if (readOffsets != null) { readOffsets.add(i); } } } LOG.debug("visibleReadOffsets:", visibleReadOffsets); return visibleReadOffsets; } @NotNull private Map<PsiVariable, Set<PsiElement>> getReachableAfterWrite(@NotNull Map<PsiVariable, IntList> writeOffsets, @NotNull Map<PsiVariable, IntList> visibleReadOffsets) { final Map<PsiVariable, Set<PsiElement>> afterWrite = new HashMap<>(); final IntFunction<BitSet> calculator = getReachableInstructionsCalculator(); for (PsiVariable variable : visibleReadOffsets.keySet()) { final BitSet collectedOffsets = new BitSet(flowEnd); for (int writeOffset : writeOffsets.get(variable).toIntArray()) { LOG.assertTrue(writeOffset >= flowStart, "writeOffset"); final BitSet reachableOffsets = calculator.apply(writeOffset); //skip current assignment reachableOffsets.set(writeOffset, false); if (writeOffset + 1 < flow.getSize()) { final PsiElement nextOffsetElement = flow.getElement(writeOffset + 1); if (nextOffsetElement instanceof PsiExpressionStatement) { if (flow.getElement(writeOffset) == ((PsiExpressionStatement)nextOffsetElement).getExpression()) { reachableOffsets.set(writeOffset + 1, false); } } } collectedOffsets.or(reachableOffsets); } Set<PsiElement> throwSources = afterWrite.getOrDefault(variable, new HashSet<>()); for (int i = flowStart; i < flowEnd; i++) { if (collectedOffsets.get(i)) { throwSources.add(flow.getElement(i)); } } final List<PsiElement> subordinates = new ArrayList<>(); for (PsiElement element : throwSources) { if (throwSources.contains(element.getParent())) { subordinates.add(element); } } subordinates.forEach(throwSources::remove); if (throwSources.isEmpty()) { afterWrite.remove(variable); } else { afterWrite.put(variable, throwSources); } } LOG.debug("afterWrite:", afterWrite); return afterWrite; } @NotNull private IntList getCatchOrFinallyOffsets(@NotNull List<? extends PsiTryStatement> tryStatements, @NotNull List<? extends PsiClassType> thrownExceptions) { final IntList catchOrFinallyOffsets = new IntArrayList(); for (PsiTryStatement tryStatement : tryStatements) { final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); if (finallyBlock != null) { int offset = flow.getStartOffset(finallyBlock); if (offset >= 0) { catchOrFinallyOffsets.add(offset - 2); // -2 is an adjustment for rethrow-after-finally } } for (PsiCatchSection catchSection : tryStatement.getCatchSections()) { final PsiCodeBlock catchBlock = catchSection.getCatchBlock(); final PsiParameter parameter = catchSection.getParameter(); if (catchBlock != null && parameter != null) { for (PsiClassType throwType : thrownExceptions) { if (isCaughtExceptionType(throwType, parameter.getType())) { int offset = flow.getStartOffset(catchBlock); if (offset >= 0) { catchOrFinallyOffsets.add(offset - 1); // -1 is an adjustment for catch block initialization } } } } } } return catchOrFinallyOffsets; } private boolean isAnyReadOffsetReachableFrom(@Nullable IntList readOffsets, @NotNull IntList fromOffsets) { if (readOffsets != null && !readOffsets.isEmpty()) { final int[] readOffsetsArray = readOffsets.toIntArray(); for (int j = 0; j < fromOffsets.size(); j++) { int fromOffset = fromOffsets.getInt(j); if (areInstructionsReachable(flow, readOffsetsArray, fromOffset)) { LOG.debug("reachableFromOffset:", fromOffset); return true; } } } return false; } @NotNull private IntFunction<BitSet> getReachableInstructionsCalculator() { final ControlFlowGraph graph = new ControlFlowGraph(flow.getSize()) { @Override void addArc(int offset, int nextOffset) { nextOffset = promoteThroughGotoChain(flow, nextOffset); if (nextOffset >= flowStart && nextOffset < flowEnd) { super.addArc(offset, nextOffset); } } }; graph.buildFrom(flow); return startOffset -> { BitSet visitedOffsets = new BitSet(flowEnd); graph.depthFirstSearch(startOffset, visitedOffsets); return visitedOffsets; }; } } final Worker worker = new Worker(); final Map<PsiVariable, IntList> writeOffsets = worker.getWritesOffsets(); if (writeOffsets.isEmpty()) return false; final PsiElement commonParent = elements.length != 1 ? PsiTreeUtil.findCommonParent(elements) : elements[0].getParent(); final List<PsiTryStatement> tryStatements = collectTryStatementStack(commonParent, enclosingCodeFragment); if (tryStatements.isEmpty()) return false; final PsiCodeBlock tryBlock = tryStatements.get(0).getTryBlock(); if (tryBlock == null) return false; final Map<PsiVariable, IntList> visibleReadOffsets = worker.getVisibleReadsOffsets(writeOffsets, tryBlock); if (visibleReadOffsets.isEmpty()) return false; final Map<PsiVariable, Set<PsiElement>> afterWrite = worker.getReachableAfterWrite(writeOffsets, visibleReadOffsets); if (afterWrite.isEmpty()) return false; final PsiClassType runtimeException = PsiType.getJavaLangRuntimeException(tryBlock.getManager(), tryBlock.getResolveScope()); final boolean runtimeExceptionIsCaught = ContainerUtil. exists(tryStatements, (tryStatement) -> isExceptionCaught(tryStatement, runtimeException)); for (Map.Entry<PsiVariable, Set<PsiElement>> entry : afterWrite.entrySet()) { final PsiVariable variable = entry.getKey(); final PsiElement[] psiElements = entry.getValue().toArray(PsiElement.EMPTY_ARRAY); final List<PsiClassType> thrownExceptions = ExceptionUtil.getThrownExceptions(psiElements); if (runtimeExceptionIsCaught) { thrownExceptions.add(runtimeException); } if (!thrownExceptions.isEmpty()) { final IntList catchOrFinallyOffsets = worker.getCatchOrFinallyOffsets(tryStatements, thrownExceptions); if (worker.isAnyReadOffsetReachableFrom(visibleReadOffsets.get(variable), catchOrFinallyOffsets)) { return true; } } } return false; }