private function traverseCall(\PHPParser_Node $node, LinkedFlowScope $scope)
 {
     assert($node instanceof \PHPParser_Node_Expr_MethodCall || $node instanceof \PHPParser_Node_Expr_FuncCall || $node instanceof \PHPParser_Node_Expr_StaticCall);
     $scope = $this->traverseChildren($node, $scope);
     // Propagate type for constants
     if (NodeUtil::isConstantDefinition($node)) {
         $constantName = $node->args[0]->value->value;
         if ($constant = $this->typeRegistry->getConstant($constantName)) {
             $constant->setPhpType($this->getType($node->args[1]->value));
         }
     }
     // If the assert function is called inside a block node, we are just going to assume
     // that some sort of exception is thrown if the assert fails (and it will not silently
     // be ignored).
     // TODO: This needs to be extracted as a general concept where we can have different
     //       effects for functions/methods. For example, array_pop affects the known item
     //       types of arrays on which it is called.
     if ($node->getAttribute('parent') instanceof BlockNode && NodeUtil::isMaybeFunctionCall($node, 'assert') && isset($node->args[0])) {
         $scope = $this->reverseInterpreter->getPreciserScopeKnowingConditionOutcome($node->args[0]->value, $scope, true);
     }
     $returnType = null;
     if (null !== ($function = $this->typeRegistry->getCalledFunctionByNode($node))) {
         $node->setAttribute('returns_by_ref', $function->isReturnByRef());
         $argValues = $argTypes = array();
         foreach ($node->args as $arg) {
             $argValues[] = $arg->value;
             $argTypes[] = $this->getType($arg);
         }
         if ($function instanceof ContainerMethodInterface) {
             $objType = $function->getContainer();
             $maybeReturnType = $function->getReturnType();
             if (null !== ($restrictedType = $this->methodInterpreter->getPreciserMethodReturnTypeKnowingArguments($function, $argValues, $argTypes))) {
                 $maybeReturnType = $restrictedType;
             }
             $returnType = $this->updateThisReference($node instanceof \PHPParser_Node_Expr_MethodCall ? $node->var : $node->class, $scope->getTypeOfThis(), $objType, $maybeReturnType);
         } else {
             if ($function instanceof GlobalFunction) {
                 if (null !== ($restrictedType = $this->functionInterpreter->getPreciserFunctionReturnTypeKnowingArguments($function, $argValues, $argTypes))) {
                     $returnType = $restrictedType;
                 } else {
                     if (null === $returnType) {
                         $returnType = $function->getReturnType();
                     }
                 }
             } else {
                 throw new \LogicException(sprintf('Unknown function "%s".', get_class($function)));
             }
         }
     }
     $node->setAttribute('type', $returnType ?: $this->typeRegistry->getNativeType('unknown'));
     return $scope;
 }
 private function checkForTypeFunctionCall(\PHPParser_Node $node, LinkedFlowScope $blindScope, $outcome)
 {
     if ($node instanceof \PHPParser_Node_Stmt_Case) {
         $left = $node->getAttribute('parent')->cond;
         $right = $node->cond;
     } else {
         $left = $node->left;
         $right = $node->right;
     }
     // Handle gettype() function calls.
     $gettypeNode = $stringNode = null;
     if (NodeUtil::isGetTypeFunctionCall($left) && $right instanceof \PHPParser_Node_Scalar_String) {
         $gettypeNode = $left;
         $stringNode = $right;
     } else {
         if (NodeUtil::isGetTypeFunctionCall($right) && $left instanceof \PHPParser_Node_Scalar_String) {
             $gettypeNode = $right;
             $stringNode = $left;
         }
     }
     if (null !== $gettypeNode && null !== $stringNode && isset($gettypeNode->args[0])) {
         $operandNode = $gettypeNode->args[0]->value;
         $operandType = $this->getTypeIfRefinable($operandNode, $blindScope);
         if (null !== $operandType) {
             $resultEqualsValue = $node instanceof \PHPParser_Node_Expr_Equal || $node instanceof \PHPParser_Node_Expr_Identical || $node instanceof \PHPParser_Node_Stmt_Case;
             if (!$outcome) {
                 $resultEqualsValue = !$resultEqualsValue;
             }
             return $this->caseGettypeFunctionCall($operandNode, $operandType, $stringNode->value, $resultEqualsValue, $blindScope);
         }
     }
     // Handle get_class() function calls.
     $getClassNode = $stringNode = null;
     if ('get_class' === NodeUtil::getFunctionName($left) && $right instanceof \PHPParser_Node_Scalar_String) {
         $getClassNode = $left;
         $stringNode = $right;
     } else {
         if ('get_class' === NodeUtil::getFunctionName($right) && $left instanceof \PHPParser_Node_Scalar_String) {
             $getClassNode = $right;
             $stringNode = $left;
         }
     }
     if (null !== $getClassNode && null !== $stringNode && isset($getClassNode->args[0])) {
         $operandNode = $getClassNode->args[0]->value;
         $operandType = $this->getTypeIfRefinable($operandNode, $blindScope);
         if (null !== $operandType) {
             $resultEqualsValue = $node instanceof \PHPParser_Node_Expr_Equal || $node instanceof \PHPParser_Node_Expr_Identical || $node instanceof \PHPParser_Node_Stmt_Case;
             if (!$outcome) {
                 $resultEqualsValue = !$resultEqualsValue;
             }
             return $this->caseGetClassFunctionCall($operandNode, $operandType, $stringNode->value, $resultEqualsValue, $blindScope);
         }
     }
     // Handle is_??? and assert function calls as well as instanceof checks.
     $typeFunctionNode = $booleanNode = null;
     if ((NodeUtil::isTypeFunctionCall($left) || NodeUtil::isMaybeFunctionCall($left, 'assert') || $left instanceof \PHPParser_Node_Expr_Instanceof) && NodeUtil::isBoolean($right)) {
         $typeFunctionNode = $left;
         $booleanNode = $right;
     } else {
         if ((NodeUtil::isTypeFunctionCall($right) || NodeUtil::isMaybeFunctionCall($right, 'assert') || $right instanceof \PHPParser_Node_Expr_Instanceof) && NodeUtil::isBoolean($left)) {
             $typeFunctionNode = $right;
             $booleanNode = $left;
         }
     }
     if (null !== $booleanNode && null !== $typeFunctionNode) {
         $expectedOutcome = NodeUtil::getBooleanValue($booleanNode) ? $outcome : !$outcome;
         if ($typeFunctionNode instanceof \PHPParser_Node_Expr_Instanceof) {
             return $this->firstPreciserScopeKnowingConditionOutcome($typeFunctionNode, $blindScope, $expectedOutcome);
         }
         if (isset($typeFunctionNode->args[0])) {
             if (NodeUtil::isMaybeFunctionCall($typeFunctionNode, 'assert')) {
                 return $this->firstPreciserScopeKnowingConditionOutcome($typeFunctionNode->args[0]->value, $blindScope, $expectedOutcome);
             }
             return $this->caseTypeFunctionCall($typeFunctionNode->name, $typeFunctionNode->args[0], $blindScope, $expectedOutcome);
         }
     }
     return null;
 }
 /**
  * Computes the GEN and KILL set.
  *
  * @param boolean $conditional
  */
 private function computeGenKill(\PHPParser_Node $node, BitSet $gen, BitSet $kill, $conditional)
 {
     switch (true) {
         case $node instanceof \PHPParser_Node_Expr_Closure:
             foreach ($node->uses as $use) {
                 assert($use instanceof \PHPParser_Node_Expr_ClosureUse);
                 $this->addToSetIfLocal($use->var, $gen);
             }
             return;
         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_If:
             $this->computeGenKill($node->cond, $gen, $kill, $conditional);
             return;
         case $node instanceof \PHPParser_Node_Stmt_Foreach:
             if (null !== $node->keyVar) {
                 if ($node->keyVar instanceof \PHPParser_Node_Expr_Variable) {
                     $this->addToSetIfLocal($node->keyVar, $kill);
                     $this->addToSetIfLocal($node->keyVar, $gen);
                 } else {
                     $this->computeGenKill($node->keyVar, $gen, $kill, $conditional);
                 }
             }
             if ($node->valueVar instanceof \PHPParser_Node_Expr_Variable) {
                 $this->addToSetIfLocal($node->valueVar, $kill);
                 $this->addToSetIfLocal($node->valueVar, $gen);
             } else {
                 $this->computeGenKill($node->valueVar, $gen, $kill, $conditional);
             }
             $this->computeGenKill($node->expr, $gen, $kill, $conditional);
             return;
         case $node instanceof \PHPParser_Node_Stmt_For:
             foreach ($node->cond as $cond) {
                 $this->computeGenKill($cond, $gen, $kill, $conditional);
             }
             return;
         case $node instanceof \PHPParser_Node_Expr_BooleanAnd:
         case $node instanceof \PHPParser_Node_Expr_BooleanOr:
             $this->computeGenKill($node->left, $gen, $kill, $conditional);
             // May short circuit.
             $this->computeGenKill($node->right, $gen, $kill, true);
             return;
         case $node instanceof \PHPParser_Node_Expr_Ternary:
             $this->computeGenKill($node->cond, $gen, $kill, $conditional);
             // Assume both sides are conditional.
             if (null !== $node->if) {
                 $this->computeGenKill($node->if, $gen, $kill, true);
             }
             $this->computeGenKill($node->else, $gen, $kill, true);
             return;
         case $node instanceof \PHPParser_Node_Param:
             $this->markAllParametersEscaped();
             return;
         case $node instanceof \PHPParser_Node_Expr_Variable:
             if (!is_string($node->name)) {
                 $this->addAllToSetIfLocal($gen);
                 return;
             }
             $this->addToSetIfLocal($node->name, $gen);
             return;
         case $node instanceof \PHPParser_Node_Expr_Include:
             $this->computeGenKill($node->expr, $gen, $kill, $conditional);
             if ($node->type === \PHPParser_Node_Expr_Include::TYPE_INCLUDE || $node->type === \PHPParser_Node_Expr_Include::TYPE_REQUIRE) {
                 $this->addAllToSetIfLocal($gen);
             }
             return;
         case NodeUtil::isMaybeFunctionCall($node, 'get_defined_vars'):
             $this->addAllToSetIfLocal($gen);
             break;
         case NodeUtil::isMaybeFunctionCall($node, 'compact'):
             foreach ($node->args as $arg) {
                 $this->computeGenKill($arg, $gen, $kill, $conditional);
             }
             $varNames = array();
             $isDynamic = false;
             foreach ($node->args as $arg) {
                 if ($arg->value instanceof \PHPParser_Node_Scalar_String) {
                     $varNames[] = $arg->value->value;
                     continue;
                 }
                 if ($arg->value instanceof \PHPParser_Node_Expr_Array) {
                     foreach ($arg->value->items as $item) {
                         assert($item instanceof \PHPParser_Node_Expr_ArrayItem);
                         if ($item->value instanceof \PHPParser_Node_Scalar_String) {
                             $varNames[] = $item->value->value;
                             continue;
                         }
                         $isDynamic = true;
                         break 2;
                     }
                     continue;
                 }
                 $isDynamic = true;
                 break;
             }
             $this->addAllToSetIfLocal($gen, $isDynamic ? null : $varNames);
             return;
         case $node instanceof \PHPParser_Node_Expr_AssignList:
             foreach ($node->vars as $var) {
                 if (!$var instanceof \PHPParser_Node_Expr_Variable) {
                     continue;
                 }
                 if (!$conditional) {
                     $this->addToSetIfLocal($var->name, $kill);
                 }
                 $this->addToSetIfLocal($var->name, $gen);
             }
             $this->computeGenKill($node->expr, $gen, $kill, $conditional);
             return;
             // AssignList is already handled in the previous CASE block.
         // AssignList is already handled in the previous CASE block.
         case NodeUtil::isAssignmentOp($node) && $node->var instanceof \PHPParser_Node_Expr_Variable:
             if ($node->var->name instanceof \PHPParser_Node_Expr) {
                 $this->computeGenKill($node->var->name, $gen, $kill, $conditional);
             }
             if (!$conditional) {
                 $this->addToSetIfLocal($node->var->name, $kill);
             }
             if (!$node instanceof \PHPParser_Node_Expr_Assign) {
                 // Assignments such as a += 1 reads a first.
                 $this->addToSetIfLocal($node->var->name, $gen);
             }
             $this->computeGenKill($node->expr, $gen, $kill, $conditional);
             return;
         default:
             foreach ($node as $subNode) {
                 if (is_array($subNode)) {
                     foreach ($subNode as $aSubNode) {
                         if (!$aSubNode instanceof \PHPParser_Node) {
                             continue;
                         }
                         $this->computeGenKill($aSubNode, $gen, $kill, $conditional);
                     }
                     continue;
                 } else {
                     if (!$subNode instanceof \PHPParser_Node) {
                         continue;
                     }
                 }
                 $this->computeGenKill($subNode, $gen, $kill, $conditional);
             }
     }
 }