@NotNull public Pair<Set<Instruction>, Set<Instruction>> getConstConditionalExpressions() { Set<Instruction> trueSet = new HashSet<Instruction>(); Set<Instruction> falseSet = new HashSet<Instruction>(); for (Instruction instruction : myInstructions) { if (instruction instanceof BranchingInstruction) { BranchingInstruction branchingInstruction = (BranchingInstruction) instruction; if (branchingInstruction.getPsiAnchor() != null && branchingInstruction.isConditionConst()) { if (!branchingInstruction.isTrueReachable()) { falseSet.add(branchingInstruction); } if (!branchingInstruction.isFalseReachable()) { trueSet.add(branchingInstruction); } } } } for (Instruction instruction : myInstructions) { if (instruction instanceof BranchingInstruction) { BranchingInstruction branchingInstruction = (BranchingInstruction) instruction; if (branchingInstruction.isTrueReachable()) { falseSet.remove(branchingInstruction); } if (branchingInstruction.isFalseReachable()) { trueSet.remove(branchingInstruction); } } } return Pair.create(trueSet, falseSet); }
private void handleBranchingInstruction( ProblemsHolder holder, StandardInstructionVisitor visitor, Set<Instruction> trueSet, Set<Instruction> falseSet, HashSet<PsiElement> reportedAnchors, BranchingInstruction instruction, final boolean onTheFly) { PsiElement psiAnchor = instruction.getPsiAnchor(); boolean underBinary = isAtRHSOfBooleanAnd(psiAnchor); if (instruction instanceof InstanceofInstruction && visitor.isInstanceofRedundant((InstanceofInstruction) instruction)) { if (visitor.canBeNull((BinopInstruction) instruction)) { holder.registerProblem( psiAnchor, InspectionsBundle.message("dataflow.message.redundant.instanceof"), new RedundantInstanceofFix()); } else { final LocalQuickFix localQuickFix = createSimplifyBooleanExpressionFix(psiAnchor, true); holder.registerProblem( psiAnchor, InspectionsBundle.message( underBinary ? "dataflow.message.constant.condition.when.reached" : "dataflow.message.constant.condition", Boolean.toString(true)), localQuickFix == null ? null : new LocalQuickFix[] {localQuickFix}); } } else if (psiAnchor instanceof PsiSwitchLabelStatement) { if (falseSet.contains(instruction)) { holder.registerProblem( psiAnchor, InspectionsBundle.message("dataflow.message.unreachable.switch.label")); } } else if (psiAnchor != null && !reportedAnchors.contains(psiAnchor) && !isFlagCheck(psiAnchor)) { boolean evaluatesToTrue = trueSet.contains(instruction); final PsiElement parent = psiAnchor.getParent(); if (parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression) parent).getLExpression() == psiAnchor) { holder.registerProblem( psiAnchor, InspectionsBundle.message( "dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)), createConditionalAssignmentFixes( evaluatesToTrue, (PsiAssignmentExpression) parent, onTheFly)); } else if (!skipReportingConstantCondition(visitor, psiAnchor, evaluatesToTrue)) { final LocalQuickFix fix = createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue); String message = InspectionsBundle.message( underBinary ? "dataflow.message.constant.condition.when.reached" : "dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue)); holder.registerProblem(psiAnchor, message, fix == null ? null : new LocalQuickFix[] {fix}); } reportedAnchors.add(psiAnchor); } }
@Override public void visit(BranchingInstruction insn) { switch (insn.getCondition()) { case EQUAL: branch( compare(BinaryOperation.EQUALS, insn.getOperand()), insn.getConsequent(), insn.getAlternative()); break; case NOT_EQUAL: branch( compare(BinaryOperation.NOT_EQUALS, insn.getOperand()), insn.getConsequent(), insn.getAlternative()); break; case GREATER: branch( compare(BinaryOperation.GREATER, insn.getOperand()), insn.getConsequent(), insn.getAlternative()); break; case GREATER_OR_EQUAL: branch( compare(BinaryOperation.GREATER_OR_EQUALS, insn.getOperand()), insn.getConsequent(), insn.getAlternative()); break; case LESS: branch( compare(BinaryOperation.LESS, insn.getOperand()), insn.getConsequent(), insn.getAlternative()); break; case LESS_OR_EQUAL: branch( compare(BinaryOperation.LESS_OR_EQUALS, insn.getOperand()), insn.getConsequent(), insn.getAlternative()); break; case NOT_NULL: branch( Expr.binary( BinaryOperation.STRICT_NOT_EQUALS, Expr.var(insn.getOperand().getIndex()), Expr.constant(null)), insn.getConsequent(), insn.getAlternative()); break; case NULL: branch( Expr.binary( BinaryOperation.STRICT_EQUALS, Expr.var(insn.getOperand().getIndex()), Expr.constant(null)), insn.getConsequent(), insn.getAlternative()); break; } }
@NotNull final RunnerResult analyzeMethod( @NotNull PsiElement psiBlock, @NotNull InstructionVisitor visitor, boolean ignoreAssertions, @NotNull Collection<DfaMemoryState> initialStates) { if (PsiTreeUtil.findChildOfType(psiBlock, OuterLanguageElement.class) != null) return RunnerResult.NOT_APPLICABLE; try { final ControlFlow flow = new ControlFlowAnalyzer(myValueFactory, psiBlock, ignoreAssertions).buildControlFlow(); if (flow == null) return RunnerResult.NOT_APPLICABLE; int[] loopNumber = LoopAnalyzer.calcInLoop(flow); int endOffset = flow.getInstructionCount(); myInstructions = flow.getInstructions(); myNestedClosures.clear(); Set<Instruction> joinInstructions = ContainerUtil.newHashSet(); for (int index = 0; index < myInstructions.length; index++) { Instruction instruction = myInstructions[index]; if (instruction instanceof GotoInstruction) { joinInstructions.add(myInstructions[((GotoInstruction) instruction).getOffset()]); } else if (instruction instanceof ConditionalGotoInstruction) { joinInstructions.add( myInstructions[((ConditionalGotoInstruction) instruction).getOffset()]); } else if (instruction instanceof MethodCallInstruction && !((MethodCallInstruction) instruction).getContracts().isEmpty()) { joinInstructions.add(myInstructions[index + 1]); } } if (LOG.isDebugEnabled()) { LOG.debug("Analyzing code block: " + psiBlock.getText()); for (int i = 0; i < myInstructions.length; i++) { LOG.debug(i + ": " + myInstructions[i]); } } // for (int i = 0; i < myInstructions.length; i++) System.out.println(i + ": " + // myInstructions[i].toString()); Integer tooExpensiveHash = psiBlock.getUserData(TOO_EXPENSIVE_HASH); if (tooExpensiveHash != null && tooExpensiveHash == psiBlock.getText().hashCode()) { LOG.debug("Too complex because hasn't changed since being too complex already"); return RunnerResult.TOO_COMPLEX; } final StateQueue queue = new StateQueue(); for (final DfaMemoryState initialState : initialStates) { queue.offer(new DfaInstructionState(myInstructions[0], initialState)); } MultiMap<BranchingInstruction, DfaMemoryState> processedStates = MultiMap.createSet(); MultiMap<BranchingInstruction, DfaMemoryState> incomingStates = MultiMap.createSet(); long msLimit = shouldCheckTimeLimit() ? Registry.intValue("ide.dfa.time.limit.online") : Registry.intValue("ide.dfa.time.limit.offline"); WorkingTimeMeasurer measurer = new WorkingTimeMeasurer(msLimit * 1000 * 1000); int count = 0; while (!queue.isEmpty()) { List<DfaInstructionState> states = queue.getNextInstructionStates(joinInstructions); for (DfaInstructionState instructionState : states) { if (count++ % 1024 == 0 && measurer.isTimeOver()) { LOG.debug("Too complex because the analysis took too long"); psiBlock.putUserData(TOO_EXPENSIVE_HASH, psiBlock.getText().hashCode()); return RunnerResult.TOO_COMPLEX; } ProgressManager.checkCanceled(); if (LOG.isDebugEnabled()) { LOG.debug(instructionState.toString()); } // System.out.println(instructionState.toString()); Instruction instruction = instructionState.getInstruction(); if (instruction instanceof BranchingInstruction) { BranchingInstruction branching = (BranchingInstruction) instruction; Collection<DfaMemoryState> processed = processedStates.get(branching); if (processed.contains(instructionState.getMemoryState())) { continue; } if (processed.size() > MAX_STATES_PER_BRANCH) { LOG.debug("Too complex because too many different possible states"); return RunnerResult.TOO_COMPLEX; // Too complex :( } if (loopNumber[branching.getIndex()] != 0) { processedStates.putValue(branching, instructionState.getMemoryState().createCopy()); } } DfaInstructionState[] after = acceptInstruction(visitor, instructionState); for (DfaInstructionState state : after) { Instruction nextInstruction = state.getInstruction(); if (nextInstruction.getIndex() >= endOffset) { continue; } handleStepOutOfLoop( instruction, nextInstruction, loopNumber, processedStates, incomingStates, states, after, queue); if (nextInstruction instanceof BranchingInstruction) { BranchingInstruction branching = (BranchingInstruction) nextInstruction; if (processedStates.get(branching).contains(state.getMemoryState()) || incomingStates.get(branching).contains(state.getMemoryState())) { continue; } if (loopNumber[branching.getIndex()] != 0) { incomingStates.putValue(branching, state.getMemoryState().createCopy()); } } queue.offer(state); } } } psiBlock.putUserData(TOO_EXPENSIVE_HASH, null); LOG.debug("Analysis ok"); return RunnerResult.OK; } catch (ArrayIndexOutOfBoundsException e) { LOG.error(psiBlock.getText(), e); return RunnerResult.ABORTED; } catch (EmptyStackException e) { LOG.error(psiBlock.getText(), e); return RunnerResult.ABORTED; } }