private function tryRemoveDeadAssignments(NodeTraversal $t, ControlFlowGraph $cfg)
 {
     $nodes = $cfg->getDirectedGraphNodes();
     foreach ($nodes as $cfgNode) {
         $inState = $cfgNode->getAttribute(DataFlowAnalysis::ATTR_FLOW_STATE_IN);
         $outState = $cfgNode->getAttribute(DataFlowAnalysis::ATTR_FLOW_STATE_OUT);
         $n = $cfgNode->getAstNode();
         if (null === $n) {
             continue;
         }
         switch (true) {
             case $n instanceof \PHPParser_Node_Stmt_If:
             case $n instanceof \PHPParser_Node_Stmt_ElseIf:
             case $n instanceof \PHPParser_Node_Stmt_While:
             case $n instanceof \PHPParser_Node_Stmt_Do:
             case $n instanceof \PHPParser_Node_Stmt_For:
             case $n instanceof \PHPParser_Node_Stmt_Switch:
             case $n instanceof \PHPParser_Node_Stmt_Case:
                 if (null !== ($condition = NodeUtil::getConditionExpression($n))) {
                     $this->tryRemoveAssignment($t, $condition, null, $inState, $outState);
                 }
                 continue 2;
             case $n instanceof \PHPParser_Node_Stmt_Return:
                 if (null !== $n->expr) {
                     $this->tryRemoveAssignment($t, $n->expr, null, $inState, $outState);
                 }
                 continue 2;
         }
         $this->tryRemoveAssignment($t, $n, null, $inState, $outState);
     }
 }
 private function computeMustDef(\PHPParser_Node $node, \PHPParser_Node $cfgNode, DefinitionLattice $output, $conditional)
 {
     switch (true) {
         case $node instanceof BlockNode:
         case NodeUtil::isScopeCreator($node):
             return;
         case $node instanceof \PHPParser_Node_Stmt_While:
         case $node instanceof \PHPParser_Node_Stmt_Do:
         case $node instanceof \PHPParser_Node_Stmt_For:
         case $node instanceof \PHPParser_Node_Stmt_If:
             if (null !== ($cond = NodeUtil::getConditionExpression($node))) {
                 $this->computeMustDef($cond, $cfgNode, $output, $conditional);
             }
             return;
         case $node instanceof \PHPParser_Node_Stmt_Foreach:
             if (null !== $node->keyVar && $node->keyVar instanceof \PHPParser_Node_Expr_Variable && is_string($node->keyVar->name)) {
                 $this->addToDefIfLocal($node->keyVar->name, $cfgNode, $node->expr, $output);
             }
             if ($node->valueVar instanceof \PHPParser_Node_Expr_Variable && is_string($node->valueVar->name)) {
                 $this->addToDefIfLocal($node->valueVar->name, $cfgNode, $node->expr, $output);
             }
             return;
         case $node instanceof \PHPParser_Node_Stmt_Catch:
             $this->addToDefIfLocal($node->var, $cfgNode, null, $output);
             return;
         case $node instanceof \PHPParser_Node_Expr_BooleanAnd:
         case $node instanceof \PHPParser_Node_Expr_BooleanOr:
         case $node instanceof \PHPParser_Node_Expr_LogicalAnd:
         case $node instanceof \PHPParser_Node_Expr_LogicalOr:
             $this->computeMustDef($node->left, $cfgNode, $output, $conditional);
             $this->computeMustDef($node->right, $cfgNode, $output, true);
             return;
         case $node instanceof \PHPParser_Node_Expr_Ternary:
             $this->computeMustDef($node->cond, $cfgNode, $output, $conditional);
             if (null !== $node->if) {
                 $this->computeMustDef($node->if, $cfgNode, $output, true);
             }
             $this->computeMustDef($node->else, $cfgNode, $output, true);
             return;
         case NodeUtil::isName($node):
             if (null !== ($var = $this->scope->getVar($node->name)) && isset($output[$var]) && null !== $output[$var]) {
                 $node->setAttribute('defining_expr', $output[$var]->getExpr());
             }
             return;
         default:
             if (NodeUtil::isAssignmentOp($node)) {
                 if ($node instanceof \PHPParser_Node_Expr_AssignList) {
                     $this->computeMustDef($node->expr, $cfgNode, $output, $conditional);
                     foreach ($node->vars as $var) {
                         if (null === $var) {
                             continue;
                         }
                         $this->addToDefIfLocal($var->name, $conditional ? null : $cfgNode, $node->expr, $output);
                     }
                     return;
                 }
                 if (NodeUtil::isName($node->var)) {
                     $this->computeMustDef($node->expr, $cfgNode, $output, $conditional);
                     $this->addToDefIfLocal($node->var->name, $conditional ? null : $cfgNode, $node->expr, $output);
                     return;
                 }
             }
             if (($node instanceof \PHPParser_Node_Expr_PostDec || $node instanceof \PHPParser_Node_Expr_PostInc || $node instanceof \PHPParser_Node_Expr_PreDec || $node instanceof \PHPParser_Node_Expr_PreInc) && NodeUtil::isName($node->var)) {
                 $this->addToDefIfLocal($node->var->name, $conditional ? null : $cfgNode, null, $output);
                 return;
             }
             foreach ($node as $subNode) {
                 if (is_array($subNode)) {
                     foreach ($subNode as $aSubNode) {
                         if (!$aSubNode instanceof \PHPParser_Node) {
                             continue;
                         }
                         $this->computeMustDef($aSubNode, $cfgNode, $output, $conditional);
                     }
                 } else {
                     if ($subNode instanceof \PHPParser_Node) {
                         $this->computeMustDef($subNode, $cfgNode, $output, $conditional);
                     }
                 }
             }
     }
 }
 /**
  * Calculates the out lattices for the different branches.
  *
  * Right now, we just treat ON_EX edges like UNCOND edges. If we want to be perfect, we would have to actually join
  * all the out lattices of this flow with the in lattice, and then make that the out lattice for the ON_EX edge.
  * However, that would add some extra computation for an edge case. So, the current behavior looks like a "good enough"
  * approximation.
  *
  * @param \PHPParser_Node $source
  * @param LatticeElementInterface $input
  *
  * @return array<LatticeElementInterface>
  */
 protected function branchedFlowThrough($source, LatticeElementInterface $input)
 {
     assert($source instanceof \PHPParser_Node);
     assert($input instanceof LinkedFlowScope);
     // If we have not walked a path from the entry node to this node, we cannot infer anything
     // about this scope. So, just skip it for now, we will come back later.
     if ($input === $this->bottomScope) {
         $output = $input;
     } else {
         $output = $input->createChildFlowScope();
         $output = $this->traverse($source, $output);
     }
     $condition = $conditionFlowScope = $conditionOutcomes = null;
     $result = array();
     foreach ($this->cfg->getOutEdges($source) as $branchEdge) {
         $branch = $branchEdge->getType();
         $newScope = $output;
         switch ($branch) {
             case GraphEdge::TYPE_ON_TRUE:
                 if ($source instanceof \PHPParser_Node_Stmt_Foreach) {
                     $informed = $this->traverse($source->expr, $output->createChildFlowScope());
                     $exprType = $this->getType($source->expr);
                     if (null !== $source->keyVar && $source->keyVar instanceof \PHPParser_Node_Expr_Variable) {
                         $keyType = null;
                         if ($exprType->isArrayType()) {
                             $keyType = $exprType->toMaybeArrayType()->getKeyType();
                         }
                         // If we do not have the precise key type available, we can always
                         // assume it to be either a string, or an integer.
                         $this->redeclareSimpleVar($informed, $source->keyVar, $keyType ?: $this->typeRegistry->getNativeType('generic_array_key'));
                     }
                     $valueType = $this->typeRegistry->getNativeType('unknown');
                     if ($exprType->isArrayType()) {
                         $valueType = $exprType->toMaybeArrayType()->getElementType();
                     } else {
                         if ($exprType->toMaybeObjectType()) {
                             $valueType = $exprType->toMaybeObjectType()->getTraversableElementType();
                         }
                     }
                     $source->valueVar->setAttribute('type', $valueType);
                     $this->redeclareSimpleVar($informed, $source->valueVar, $valueType);
                     $newScope = $informed;
                     break;
                 }
                 // FALL THROUGH
             // FALL THROUGH
             case GraphEdge::TYPE_ON_FALSE:
                 if (null === $condition) {
                     $condition = NodeUtil::getConditionExpression($source);
                     if (null === $condition && $source instanceof \PHPParser_Node_Stmt_Case && null !== $source->cond) {
                         $condition = $source;
                         // conditionFlowScope is cached from previous iterations of the loop
                         if (null === $conditionFlowScope) {
                             $conditionFlowScope = $this->traverse($source->cond, $output->createChildFlowScope());
                         }
                     }
                 }
                 if (null !== $condition) {
                     if ($condition instanceof \PHPParser_Node_Expr_BooleanAnd || $condition instanceof \PHPParser_Node_Expr_LogicalAnd || $condition instanceof \PHPParser_Node_Expr_BooleanOr || $condition instanceof \PHPParser_Node_Expr_LogicalOr) {
                         // When handling the short-circuiting binary operators, the outcome scope on true can be
                         // different than the outcome scope on false.
                         //
                         // TODO:
                         // The "right" way to do this is to carry the known outcome all the way through the
                         // recursive traversal, so that we can construct a different flow scope based on the outcome.
                         // However, this would require a bunch of code and a bunch of extra computation for an edge
                         // case. This seems to be a "good enough" approximation.
                         // conditionOutcomes is cached from previous iterations of the loop.
                         if (null === $conditionOutcomes) {
                             $conditionOutcomes = $condition instanceof \PHPParser_Node_Expr_BooleanAnd || $condition instanceof \PHPParser_Node_Expr_LogicalAnd ? $this->traverseAnd($condition, $output->createChildFlowScope()) : $this->traverseOr($condition, $output->createChildFlowScope());
                         }
                         $newScope = $this->reverseInterpreter->getPreciserScopeKnowingConditionOutcome($condition, $conditionOutcomes->getOutcomeFlowScope($condition, $branch === GraphEdge::TYPE_ON_TRUE), $branch === GraphEdge::TYPE_ON_TRUE);
                     } else {
                         // conditionFlowScope is cached from previous iterations of the loop.
                         if (null === $conditionFlowScope) {
                             // In case of a FOR loop, $condition might be an array of expressions. PHP will execute
                             // all expressions, but only the last one is used to determine the expression outcome.
                             if (is_array($condition)) {
                                 $conditionFlowScope = $output->createChildFlowScope();
                                 foreach ($condition as $cond) {
                                     $conditionFlowScope = $this->traverse($cond, $conditionFlowScope);
                                 }
                             } else {
                                 $conditionFlowScope = $this->traverse($condition, $output->createChildFlowScope());
                             }
                         }
                         // See above comment regarding the handling of FOR loops.
                         $relevantCondition = $condition;
                         if (is_array($condition)) {
                             $relevantCondition = end($condition);
                         }
                         $newScope = $this->reverseInterpreter->getPreciserScopeKnowingConditionOutcome($relevantCondition, $conditionFlowScope, $branch === GraphEdge::TYPE_ON_TRUE);
                     }
                 }
                 break;
         }
         if (null === $newScope) {
             throw new \LogicException('$newScope must not be null for source ' . get_class($source) . ' and branch ' . GraphEdge::getLiteral($branch) . '.');
         }
         $result[] = $newScope->optimize();
     }
     return $result;
 }
 /**
  * @param boolean $conditional
  */
 private function computeMayUse(\PHPParser_Node $node, \PHPParser_Node $cfgNode, UseLattice $output, $conditional)
 {
     switch (true) {
         case $node instanceof \JMS\PhpManipulator\PhpParser\BlockNode:
         case NodeUtil::isScopeCreator($node):
             return;
         case NodeUtil::isName($node):
             $this->addToUseIfLocal($node, $cfgNode, $output);
             return;
         case $node instanceof \PHPParser_Node_Stmt_Catch:
             $this->computeMayUse($node->stmts, $cfgNode, $output, $conditional);
             $this->addDefToVarNodes($node->var, $node, $node, $output);
             $this->removeFromUseIfLocal($node->var, $output);
             return;
         case $node instanceof \PHPParser_Node_Stmt_While:
         case $node instanceof \PHPParser_Node_Stmt_Do:
         case $node instanceof \PHPParser_Node_Stmt_If:
         case $node instanceof \PHPParser_Node_Stmt_ElseIf:
         case $node instanceof \PHPParser_Node_Stmt_For:
             if (null !== ($cond = NodeUtil::getConditionExpression($node))) {
                 $this->computeMayUse($cond, $cfgNode, $output, $conditional);
             }
             return;
         case $node instanceof \PHPParser_Node_Stmt_Foreach:
             if (!$conditional) {
                 if (null !== $node->keyVar && NodeUtil::isName($node->keyVar)) {
                     $this->removeFromUseIfLocal($node->keyVar->name, $output);
                 }
                 if (NodeUtil::isName($node->valueVar)) {
                     $this->removeFromUseIfLocal($node->valueVar->name, $output);
                 }
             }
             $this->computeMayUse($node->expr, $cfgNode, $output, $conditional);
             return;
         case $node instanceof \PHPParser_Node_Expr_BooleanAnd:
         case $node instanceof \PHPParser_Node_Expr_LogicalAnd:
         case $node instanceof \PHPParser_Node_Expr_BooleanOr:
         case $node instanceof \PHPParser_Node_Expr_LogicalOr:
             $this->computeMayUse($node->right, $cfgNode, $output, true);
             $this->computeMayUse($node->left, $cfgNode, $output, $conditional);
             return;
         case $node instanceof \PHPParser_Node_Expr_Ternary:
             $this->computeMayUse($node->else, $cfgNode, $output, true);
             if (null !== $node->if) {
                 $this->computeMayUse($node->if, $cfgNode, $output, true);
             }
             $this->computeMayUse($node->cond, $cfgNode, $output, $conditional);
             return;
         default:
             if (NodeUtil::isAssignmentOp($node)) {
                 if ($node instanceof \PHPParser_Node_Expr_AssignList) {
                     foreach ($node->vars as $var) {
                         if (null === $var) {
                             continue;
                         }
                         if (!$conditional) {
                             $this->removeFromUseIfLocal($var->name, $output);
                         }
                     }
                     $this->computeMayUse($node->expr, $cfgNode, $output, $conditional);
                     return;
                 }
                 if (NodeUtil::isName($node->var)) {
                     $this->addDefToVarNodes($node->var->name, $node->var, $node->expr, $output);
                     if (!$conditional) {
                         $this->removeFromUseIfLocal($node->var->name, $output);
                     }
                     // Handle the cases where we assign, and read at the same time, e.g.
                     // ``$a += 5``.
                     if (!$node instanceof \PHPParser_Node_Expr_Assign && !$node instanceof \PHPParser_Node_Expr_AssignRef) {
                         $this->addToUseIfLocal($node->var, $cfgNode, $output);
                     }
                     $this->computeMayUse($node->expr, $cfgNode, $output, $conditional);
                     return;
                 }
                 return;
             }
             $inOrder = array();
             foreach ($node as $subNode) {
                 if (is_array($subNode)) {
                     foreach ($subNode as $aSubNode) {
                         if (!$aSubNode instanceof \PHPParser_Node) {
                             continue;
                         }
                         $inOrder[] = $aSubNode;
                     }
                 } else {
                     if ($subNode instanceof \PHPParser_Node) {
                         $inOrder[] = $subNode;
                     }
                 }
             }
             foreach (array_reverse($inOrder) as $subNode) {
                 $this->computeMayUse($subNode, $cfgNode, $output, $conditional);
             }
     }
 }