public function serialize(ControlFlowGraph $graph)
 {
     $dot = "digraph G {\n";
     $this->ids = new \SplObjectStorage();
     $this->idCount = 0;
     $dot .= "    " . $this->getId($graph->getImplicitReturn()) . " [shape=box,label=\"implicit return\",style=filled]\n";
     $entryPoint = $graph->getEntryPoint();
     $nodes = $graph->getNodes();
     foreach ($nodes as $astNode) {
         $node = $nodes[$astNode];
         $id = $this->getId($node);
         $dot .= sprintf('    %s [shape=box,label="%s"', $id, str_replace('"', '\\"', NodeUtil::getStringRepr($astNode)));
         if ($node === $entryPoint) {
             $dot .= ",style=filled";
         }
         $dot .= "]\n";
         foreach ($node->getOutEdges() as $edge) {
             $dot .= sprintf("    %s -> %s", $id, $this->getId($edge->getDest()));
             switch ($edge->getType()) {
                 case GraphEdge::TYPE_ON_FALSE:
                     $dot .= ' [label="false"]';
                     break;
                 case GraphEdge::TYPE_ON_TRUE:
                     $dot .= ' [label="true"]';
                     break;
                 case GraphEdge::TYPE_ON_EX:
                     $dot .= ' [label="exception"]';
                     break;
             }
             $dot .= "\n";
         }
     }
     $dot .= "}";
     return $dot;
 }
 public function visit(NodeTraversal $t, \PHPParser_Node $node, \PHPParser_Node $parent = null)
 {
     // Consider only non-dynamic variables in this pass.
     if (!$node instanceof \PHPParser_Node_Expr_Variable || !is_string($node->name)) {
         return;
     }
     // We ignore PHP globals.
     if (NodeUtil::isSuperGlobal($node)) {
         return;
     }
     // Ignore variables which are defined here.
     if ($parent instanceof \PHPParser_Node_Expr_Assign && $parent->var === $node) {
         return;
     }
     if (!$t->getScope()->isDeclared($node->name)) {
         if ($bestName = CheckForTyposPass::getMostSimilarName($node->name, $t->getScope()->getVarNames())) {
             $this->phpFile->addComment($node->getLine(), Comment::error('typos.mispelled_variable_name', 'The variable ``%offending_variable_name%`` does not exist. Did you mean ``%closest_variable_name%``?', array('offending_variable_name' => '$' . $node->name, 'closest_variable_name' => '$' . $bestName)));
         } else {
             $this->phpFile->addComment($node->getLine(), Comment::warning('usage.undeclared_variable', 'The variable ``%variable_name%`` does not exist. Did you forget to declare it?', array('variable_name' => '$' . $node->name)));
         }
         return;
     }
     if ($parent instanceof \PHPParser_Node_Expr_ArrayDimFetch && $node->getAttribute('array_initializing_variable') && $parent->var === $node) {
         $this->phpFile->addComment($node->getLine(), Comment::warning('usage.non_initialized_array', '``%array_fetch_expr%`` was never initialized. Although not strictly required by PHP, it is generally a good practice to add ``%array_fetch_expr% = array();`` before regardless.', array('array_fetch_expr' => self::$prettyPrinter->prettyPrintExpr($node))));
     }
 }
 protected function analyzeStream()
 {
     $this->newContent = '';
     $getNameType = $this->registry->createUnionType(array($this->registry->getClassOrCreate('ReflectionZendExtension'), $this->registry->getClassOrCreate('ReflectionExtension'), $this->registry->getClassOrCreate('ReflectionFunction'), $this->registry->getClassOrCreate('ReflectionParameter'), $this->registry->getClassOrCreate('ReflectionClass'), $reflectionMethodType = $this->registry->getClassOrCreate('ReflectionMethod')));
     $reflectionPropertyType = $this->registry->getClassOrCreate('ReflectionProperty');
     $lastNode = null;
     while ($this->stream->moveNext()) {
         switch (true) {
             case $this->stream->node instanceof \PHPParser_Node_Expr_MethodCall && $lastNode !== $this->stream->node:
                 if (NodeUtil::isCallToMethod($this->stream->node, 'getName') && null !== ($type = $this->stream->node->var->getAttribute('type')) && $this->isSubtype($type, $getNameType)) {
                     $this->newContent .= '->name';
                     $this->stream->skipUntil('END_OF_CALL');
                     break;
                 }
                 if (NodeUtil::isCallToMethod($this->stream->node, 'getDeclaringClass') && null !== ($type = $this->stream->node->var->getAttribute('type')) && ($this->isSubtype($type, $reflectionMethodType) || $this->isSubtype($type, $reflectionPropertyType)) && ($parent = $this->stream->node->getAttribute('parent')) && NodeUtil::isCallToMethod($parent, 'getName')) {
                     $this->newContent .= '->class';
                     $this->stream->skipUntil('END_OF_CALL');
                     $this->stream->skipUntil('END_OF_CALL');
                     break;
                 }
                 $this->newContent .= $this->stream->token->getContent();
                 break;
             default:
                 $this->newContent .= $this->stream->token->getContent();
         }
         $lastNode = $this->stream->node;
     }
 }
 public function visit(NodeTraversal $t, \PHPParser_Node $n, \PHPParser_Node $parent = null)
 {
     if ($n instanceof \PHPParser_Node_Stmt_Property) {
         $containerNode = NodeUtil::getContainer($n)->get();
         $class = $this->typeRegistry->getClassByNode($containerNode);
         if ($class->isInterface()) {
             $this->phpFile->addComment($n->getLine(), Comment::error('basic_semantics.property_on_interface', 'In PHP, declaring a method on an interface is not yet allowed.'));
         }
     }
     if (NodeUtil::isMethodContainer($n)) {
         $class = $this->typeRegistry->getClassByNode($n);
         if ($class instanceof Clazz && !$class->isAbstract()) {
             $abstractMethods = array();
             foreach ($class->getMethods() as $method) {
                 assert($method instanceof ClassMethod);
                 /** @var $method ClassMethod */
                 if ($method->isAbstract()) {
                     $abstractMethods[] = $method->getMethod()->getName();
                 }
             }
             if (!empty($abstractMethods)) {
                 switch (count($abstractMethods)) {
                     case 1:
                         $this->phpFile->addComment($n->getLine(), Comment::error('basic_semantics.abstract_method_on_non_abstract_class', 'There is one abstract method ``%method%`` in this class; you could implement it, or declare this class as abstract.', array('method' => reset($abstractMethods))));
                         break;
                     default:
                         $this->phpFile->addComment($n->getLine(), Comment::error('basic_semantics.abstract_methods_on_non_abstract_class', 'There is at least one abstract method in this class. Maybe declare it as abstract, or implement the remaining methods: %methods%', array('methods' => implode(', ', $abstractMethods))));
                 }
             }
         }
     }
 }
 public function shouldTraverse(NodeTraversal $traversal, \PHPParser_Node $node, \PHPParser_Node $parent = null)
 {
     if (NodeUtil::isScopeCreator($node) && null !== $parent) {
         if ($node instanceof \PHPParser_Node_Expr_Closure) {
             $node->setAttribute('type', $this->typeRegistry->getClassOrCreate('Closure'));
         }
         return false;
     }
     return true;
 }
 public function visit(NodeTraversal $t, \PHPParser_Node $node, \PHPParser_Node $parent = null)
 {
     if (!NodeUtil::isCallLike($node)) {
         return;
     }
     if (null === ($function = $this->typeRegistry->getCalledFunctionByNode($node))) {
         return;
     }
     foreach ($function->getParameters() as $param) {
         $index = $param->getIndex();
         if (!isset($node->args[$index])) {
             continue;
         }
         $node->args[$index]->setAttribute('param_expects_ref', $param->isPassedByRef());
     }
 }
 private function getThisType(\PHPParser_Node $node, Scope $parentScope = null)
 {
     $parent = $node;
     while (null !== $parent) {
         // As of PHP 5.4, closures inherit the this type of their parent scope. Since there is currently no way to
         // configure the PHP version that we build against, we will simply always infer the ThisType. Other passes,
         // can then perform checks whether ``$this`` may be accessed, allowing us to produce better error messages.
         if ($parent instanceof \PHPParser_Node_Expr_Closure) {
             if (null === $parentScope) {
                 return null;
             }
             return $parentScope->getTypeOfThis();
         }
         if (NodeUtil::isMethodContainer($parent)) {
             $name = implode("\\", $parent->namespacedName->parts);
             return $this->typeRegistry->getClassOrCreate($name);
         }
         $parent = $parent->getAttribute('parent');
     }
     return null;
 }
 public function visit(NodeTraversal $t, \PHPParser_Node $node, \PHPParser_Node $parent = null)
 {
     if (!NodeUtil::isCallLike($node)) {
         return;
     }
     if (null === ($function = $this->typeRegistry->getCalledFunctionByNode($node))) {
         return;
     }
     foreach ($function->getParameters() as $param) {
         if (!$param->isPassedByRef()) {
             continue;
         }
         $index = $param->getIndex();
         if (!isset($node->args[$index])) {
             continue;
         }
         $arg = $node->args[$index];
         if (!NodeUtil::canBePassedByRef($arg->value)) {
             $this->phpFile->addComment($arg->getLine(), Comment::error('usage.non_referencable_arg', '``%argument%`` cannot be passed to ``%function_name%()`` as the parameter ``$%parameter_name%`` expects a reference.', array('argument' => self::$prettyPrinter->prettyPrintExpr($arg->value), 'function_name' => $function->getName(), 'parameter_name' => $param->getName())));
         }
     }
 }
 /**
  * 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);
             }
     }
 }
 private function addToDefIfLocal($name, \PHPParser_Node $node = null, \PHPParser_Node $rValue = null, DefinitionLattice $definitions)
 {
     $var = $this->scope->getVar($name);
     if (null === $var) {
         return;
     }
     // Invalidate other definitions if they depended on this variable as it has been changed.
     foreach ($definitions as $otherVar) {
         if (null === ($otherDef = $definitions[$otherVar])) {
             continue;
         }
         if ($otherDef->dependsOn($var)) {
             $definitions[$otherVar] = null;
         }
     }
     // The node can be null if we are dealing with a conditional definition. For conditional
     // definitions, this analysis cannot do much.
     if (null === $node) {
         $definitions[$var] = null;
         return;
     }
     $definition = new Definition($node, $rValue);
     if (null !== $rValue) {
         NodeTraversal::traverseWithCallback($rValue, new PreOrderCallback(function ($t, \PHPParser_Node $node) use($definition) {
             if (NodeUtil::isScopeCreator($node)) {
                 return false;
             }
             if (!NodeUtil::isName($node)) {
                 return;
             }
             if (null === ($var = $this->scope->getVar($node->name))) {
                 if (null !== $this->logger) {
                     $this->logger->debug(sprintf('Could not find variable "%s" in the current scope. ' . 'This could imply an error in SyntacticScopeCreator.', $node->name));
                 }
                 // We simply assume that the variable was declared so that we can properly
                 // invalidate the definition if it is changed. For this analysis, it does not
                 // matter whether it actually exists. A later pass will add a warning anyway.
                 $var = $this->scope->declareVar($node->name);
             }
             $definition->addDependentVar($var);
         }));
     }
     $definitions[$var] = $definition;
 }
 private function narrowMaybeConditionScope(\PHPParser_Node $expr, LinkedFlowScope $blindScope, $condition)
 {
     // In contrast to a non-short-circuiting condition scope, for may-be short-circuiting scopes,
     // we make the simplification to not narrow down the expression, and the defining expression
     // by always using the defining expression in case the variable is of boolean type and never
     // if it has a different type.
     if (NodeUtil::isName($expr) && null !== ($defExpr = $expr->getAttribute('defining_expr')) && null !== ($exprType = $expr->getAttribute('type')) && $exprType->isBooleanType()) {
         return $this->firstPreciserScopeKnowingConditionOutcome($defExpr, $blindScope, $condition);
     }
     return $this->firstPreciserScopeKnowingConditionOutcome($expr, $blindScope, $condition);
 }
 private function enterNode(\PHPParser_Node $node)
 {
     if (NodeUtil::isMethodContainer($node)) {
         $this->commentParser->setCurrentClassName(implode("\\", $node->namespacedName->parts));
         $this->commentParser->setImportedNamespaces($this->importedNamespaces);
         $this->classParser->setImportedNamespaces($this->importedNamespaces);
         $class = $this->classParser->parse($node);
         $this->classFiles[$class] = $this->phpFile;
         if ($this->typeRegistry->hasClass($class->getName(), TypeRegistry::LOOKUP_NO_CACHE)) {
             $this->analyzer->logger->warning(sprintf('The class "%s" has been defined more than once (maybe as a fixture for code generation). Ignoring the second definition.', $class->getName()));
             return;
         }
         $this->typeRegistry->registerClass($class);
     } else {
         if ($node instanceof \PHPParser_Node_Stmt_Function) {
             $this->functionParser->setImportedNamespaces($this->importedNamespaces);
             $function = $this->functionParser->parse($node);
             if ($this->typeRegistry->hasFunction($functionName = $function->getName(), false)) {
                 $this->analyzer->logger->warning(sprintf('The function "%s" has been defined more than once (probably conditionally). Ignoring the second definition.', $functionName));
                 return;
             }
             $this->typeRegistry->registerFunction($function);
         } else {
             if (NodeUtil::isConstantDefinition($node)) {
                 assert($node instanceof \PHPParser_Node_Expr_FuncCall);
                 if (!$node->args[0]->value instanceof \PHPParser_Node_Scalar_String) {
                     return;
                 }
                 $constant = new GlobalConstant($node->args[0]->value->value);
                 $constant->setAstNode($node);
                 $constant->setPhpType($this->typeRegistry->getNativeType('unknown'));
                 if (null !== ($type = $node->args[1]->value->getAttribute('type'))) {
                     $constant->setPhpType($type);
                 }
                 if ($this->typeRegistry->hasConstant($constant->getName(), false)) {
                     $this->analyzer->logger->warning(sprintf('The constant "%s" was defined more than once. Ignoring all but first definition.', $constant->getName()));
                     return;
                 }
                 $this->typeRegistry->registerConstant($constant);
             }
         }
     }
 }
 public function __toString()
 {
     $str = 'DefinitionLattice(';
     $defs = array();
     foreach ($this->definitions as $var) {
         $defs[$var->getName()] = null === $this->definitions[$var] ? null : NodeUtil::getStringRepr($this->definitions[$var]->getNode());
     }
     $str .= json_encode($defs) . ')';
     return $str;
 }
 private function handleGoto(\PHPParser_Node_Stmt_Goto $node)
 {
     $parent = $node->getAttribute('parent');
     while (!NodeUtil::isScopeCreator($parent)) {
         $newParent = $parent->getAttribute('parent');
         if (null === $newParent) {
             break;
         }
         $parent = $newParent;
     }
     $nodes = \PHPParser_Finder::create(array($parent))->find('Stmt_Label[name=' . $node->name . ']');
     if (!$nodes) {
         return;
     }
     $this->graph->connectIfNotConnected($node, GraphEdge::TYPE_UNCOND, $nodes[0]);
 }
 private function assertNotMatch($code)
 {
     $this->computeDefUse($code);
     $this->assertNotSame($this->def, $this->analysis->getDefiningNode('x', $this->use), 'Found definition was not expected: ' . \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::getStringRepr($this->def));
 }
 private function isRewritable(\PHPParser_Node $startNode, AbstractToken $token, array &$imports, AbstractToken &$lastToken = null, &$writeMultiple = false)
 {
     $namespaceNode = \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::findParent($startNode, 'PHPParser_Node_Stmt_Namespace') ?: $this->fixedFile->getAst();
     $preserveMultiple = $this->getSetting('preserve_multiple');
     $matchedAtLeastOne = false;
     $writeMultiple = false;
     while ($token->matches(T_USE)) {
         $currentImports = array();
         $currentLastToken = null;
         $currentMultiple = false;
         if (!$this->isSafeToRewrite($token, $currentImports, $currentLastToken, $currentMultiple)) {
             break;
         }
         // If we are supposed to preserve multiples, and we already matched
         // some single use statements, bail out here. We will come back when
         // reaching the T_USE of the multiple import statement.
         if ($currentMultiple && $matchedAtLeastOne && $preserveMultiple) {
             break;
         }
         $this->removeUnused($namespaceNode, $currentImports);
         $matchedAtLeastOne = true;
         $imports = array_merge($imports, $currentImports);
         $lastToken = $currentLastToken;
         // If we scanned a multiple imports use statement, and we are supposed
         // to preserve it, bail out here as we have already reached its end.
         if ($currentMultiple && $preserveMultiple) {
             $writeMultiple = true;
             return true;
         }
         $token = $lastToken->findNextToken('NO_WHITESPACE')->get();
     }
     return $matchedAtLeastOne;
 }
 private function tryRemoveAssignment(NodeTraversal $t, \PHPParser_Node $n, \PHPParser_Node $exprRoot = null, $inState, $outState)
 {
     $parent = $n->getAttribute('parent');
     if (NodeUtil::isAssignmentOp($n)) {
         $lhs = $n->var;
         $rhs = $n->expr;
         if ($n instanceof \PHPParser_Node_Expr_AssignList) {
             $i = 0;
             foreach ($n->vars as $var) {
                 if (null === $var) {
                     $i += 1;
                     continue;
                 }
                 $newNode = new \PHPParser_Node_Expr_Assign($var, new \PHPParser_Node_Expr_ArrayDimFetch($rhs, new \PHPParser_Node_Scalar_LNumber($i)), $n->getLine());
                 $newNode->setAttribute('is_list_assign', true);
                 $this->tryRemoveAssignment($t, $newNode, $exprRoot, $inState, $outState);
                 $i += 1;
             }
             return;
         }
         // Recurse first. Example: dead_x = dead_y = 1; We try to clean up dead_y first.
         if (null !== $rhs) {
             $this->tryRemoveAssignment($t, $rhs, $exprRoot, $inState, $outState);
             $rhs = $lhs->getAttribute('next');
         }
         $scope = $t->getScope();
         if (!$lhs instanceof \PHPParser_Node_Expr_Variable || !is_string($lhs->name)) {
             return;
         }
         if (!$scope->isDeclared($lhs->name)) {
             return;
         }
         if (in_array($lhs->name, NodeUtil::$superGlobalNames, true)) {
             return;
         }
         $var = $scope->getVar($lhs->name);
         $escaped = $this->liveness->getEscapedLocals();
         if (isset($escaped[$var])) {
             return;
             // Local variable that might be escaped due to closures.
         }
         // If we have an identity assignment such as a=a, always remove it
         // regardless of what the liveness results because it does not
         // change the result afterward.
         if (null !== $rhs && $rhs instanceof \PHPParser_Node_Expr_Variable && $var->getName() === $rhs->name && $n instanceof \PHPParser_Node_Expr_Assign) {
             $this->phpFile->addComment($n->getLine(), Comment::warning('dead_assignment.assignment_to_itself', 'Why assign ``%variable%`` to itself?', array('variable' => '$' . $rhs->name)));
             return;
         }
         // At the moment we miss some dead assignments if a variable is killed,
         // and defined in the same node of the control flow graph.
         // Example: $a = 'foo'; return $a = 'bar';
         if ($outState->isLive($var)) {
             return;
             // Variable is not dead.
         }
         // Assignments to references do not need to be used in the
         // current scope.
         if ($var->isReference()) {
             return;
         }
         //             if ($inState->isLive($var)
         //                    /*&& $this->isVariableStillLiveWithinExpression($n, $exprRoot, $var->getName()) */) {
         // The variable is killed here but it is also live before it.
         // This is possible if we have say:
         //    if ($X = $a && $a = $C) {..} ; .......; $a = $S;
         // In this case we are safe to remove "$a = $C" because it is dead.
         // However if we have:
         //    if ($a = $C && $X = $a) {..} ; .......; $a = $S;
         // removing "a = C" is NOT correct, although the live set at the node
         // is exactly the same.
         // TODO: We need more fine grain CFA or we need to keep track
         // of GEN sets when we recurse here. Maybe add the comment anyway, and let the user decide?
         //                 return;
         //             }
         if ($n instanceof \PHPParser_Node_Expr_Assign) {
             if ($n->getAttribute('is_list_assign', false)) {
                 $this->phpFile->addComment($n->getLine(), Comment::warning('dead_assignment.unnecessary_list_assign', 'The assignment to ``$%variable%`` is dead. Consider omitting it like so ``list($first,,$third)``.', array('variable' => $var->getName())));
                 return;
             }
             $this->phpFile->addComment($n->getLine(), Comment::warning('dead_assignment.unnecessary_var_assign', 'The assignment to ``$%variable%`` is dead and can be removed.', array('variable' => $var->getName())));
             return;
         }
     }
 }
 private function scanVars(Node $node, Node $parent)
 {
     switch (true) {
         case $node instanceof \PHPParser_Node_Stmt_StaticVar:
             $this->declareVar($node->name, null, true, $node);
             return;
         case $node instanceof \PHPParser_Node_Expr_Variable:
             if (($parent instanceof \PHPParser_Node_Expr_Assign || $parent instanceof \PHPParser_Node_Expr_AssignRef) && is_string($node->name) && $parent->var === $node) {
                 $this->declareVar($node->name, null, true, $node);
             }
             // PHP automatically creates a variable (without issuing a warning) if an item is added
             // to a variable without the variable being declared as array previously.
             // ``function() { $a[] = 'foo'; }`` is perfectly permissible, and will create ``$a``.
             if ($parent instanceof \PHPParser_Node_Expr_ArrayDimFetch && $parent->var === $node) {
                 $parentsParent = $parent->getAttribute('parent');
                 if (($parentsParent instanceof \PHPParser_Node_Expr_Assign || $parentsParent instanceof \PHPParser_Node_Expr_AssignRef) && is_string($node->name) && $parentsParent->var === $parent) {
                     // TODO: We should better track which variables have been initialized
                     //       and which have not. This can be done by adding an undefined
                     //       type for those that have not.
                     if (!$this->scope->isDeclared($node->name)) {
                         $node->setAttribute('array_initializing_variable', true);
                     }
                     $this->declareVar($node->name, null, true, $node);
                 }
             }
             if ($parent instanceof \PHPParser_Node_Arg && $parent->getAttribute('param_expects_ref', false) && is_string($node->name)) {
                 $this->declareVar($node->name, null, true, $node);
             }
             return;
         case $node instanceof \PHPParser_Node_Expr_AssignList:
             foreach ($node->vars as $var) {
                 if ($var instanceof \PHPParser_Node_Expr_Variable && is_string($var->name)) {
                     $this->declareVar($var->name, null, true, $var);
                 }
             }
             $this->scanVars($node->expr, $node);
             return;
         case $node instanceof \PHPParser_Node_Stmt_Trait:
         case $node instanceof \PHPParser_Node_Stmt_Interface:
         case $node instanceof \PHPParser_Node_Stmt_Class:
         case NodeUtil::isScopeCreator($node):
             return;
             // do not examine their children as they belong to a different scope
         // do not examine their children as they belong to a different scope
         case $node instanceof \PHPParser_Node_Stmt_Catch:
             $this->declareVar($node->var, null, true, $node);
             $this->scanVars($node->stmts, $node);
             return;
         case $node instanceof \PHPParser_Node_Stmt_Foreach:
             if ($node->keyVar !== null && $node->keyVar instanceof \PHPParser_Node_Expr_Variable && is_string($node->keyVar->name)) {
                 $this->declareVar($node->keyVar->name, null, true, $node->keyVar);
             }
             if ($node->valueVar instanceof \PHPParser_Node_Expr_Variable && is_string($node->valueVar->name)) {
                 $this->declareVar($node->valueVar->name, null, true, $node->valueVar);
             }
             $this->scanVars($node->stmts, $node);
             return;
         default:
             foreach ($node as $subNode) {
                 if (is_array($subNode)) {
                     foreach ($subNode as $aSubNode) {
                         if (!$aSubNode instanceof \PHPParser_Node) {
                             continue;
                         }
                         $this->scanVars($aSubNode, $node);
                     }
                 } else {
                     if ($subNode instanceof \PHPParser_Node) {
                         $this->scanVars($subNode, $node);
                     }
                 }
             }
     }
 }
Exemplo n.º 19
0
 /**
  * Refines an array type with the information about the used key, and assigned element type.
  *
  * This also defines the type for the item key if available and non-dynamic.
  *
  * @param PhpType $varType
  * @param PhpType $resultType
  * @param \PHPParser_Node_Expr_ArrayDimFetch $node
  *
  * @return ArrayType
  */
 private function getRefinedArrayType(PhpType $varType, PhpType $resultType, \PHPParser_Node_Expr_ArrayDimFetch $node)
 {
     $usedKeyType = $this->getKeyTypeForDimFetch($node);
     $keyType = $varType->getKeyType();
     if ($keyType === $this->typeRegistry->getNativeType('generic_array_key')) {
         $refinedKeyType = $usedKeyType;
     } else {
         $refinedKeyType = $keyType->getLeastSupertype($usedKeyType);
     }
     $elementType = $varType->getElementType();
     if ($elementType === $this->typeRegistry->getNativeType('generic_array_value')) {
         $refinedElementType = $resultType;
     } else {
         $refinedElementType = $elementType->getLeastSupertype($resultType);
     }
     $refinedType = $this->typeRegistry->getArrayType($refinedElementType, $refinedKeyType);
     // Infer the type of the key if a concrete value is available.
     NodeUtil::getValue($node->dim)->map(function ($keyName) use(&$refinedType, $resultType) {
         $refinedType = $refinedType->inferItemType($keyName, $resultType);
     });
     return $refinedType;
 }
 public function visit(NodeTraversal $t, \PHPParser_Node $node, \PHPParser_Node $parent = null)
 {
     switch (true) {
         case $node instanceof \PHPParser_Node_Expr_Variable:
             if (NodeUtil::isSuperGlobal($node)) {
                 return;
             }
             if (is_string($node->name)) {
                 $this->checkNaming($node->getLine(), '$' . $node->name, $node->name, 'local_variable');
             }
             break;
         case $node instanceof \PHPParser_Node_Stmt_PropertyProperty:
             $this->checkNaming($node->getLine(), '$' . $node->name, $node->name, 'property_name');
             break;
         case $node instanceof \PHPParser_Node_Stmt_Function:
         case $node instanceof \PHPParser_Node_Stmt_ClassMethod:
             $function = $this->typeRegistry->getFunctionByNode($node);
             // If a class method is constrained by a contract of an interface, or an abstract
             // class, then there is no point in adding warnings for them.
             if ($function instanceof ClassMethod && $function->isConstrainedByContract()) {
                 break;
             }
             $this->checkNaming($node->getLine(), 'function ' . $node->name . '()', $node->name, 'method_name');
             if (null !== $function && null !== ($returnType = $function->getReturnType()) && $returnType->isBooleanType()) {
                 $this->checkNaming($node->getLine(), 'function ' . $node->name . '()', $node->name, 'isser_method_name');
             }
             $this->checkParameters($node->params);
             break;
         case $node instanceof \PHPParser_Node_Expr_Closure:
             $this->checkParameters($node->params);
             break;
         case NodeUtil::isMethodContainer($node):
             $class = $this->typeRegistry->getClassByNode($node)->toMaybeObjectType();
             if ($class) {
                 $this->checkNaming($node->getLine(), $class->getShortName(), $class->getShortName(), 'type_name');
                 if ($class->isInterface()) {
                     $this->checkNaming($node->getLine(), $class->getShortName(), $class->getShortName(), 'interface_name');
                 } elseif ($class instanceof Clazz) {
                     if ($class->isUtilityClass()) {
                         $this->checkNaming($node->getLine(), $class->getShortName(), $class->getShortName(), 'utility_class_name');
                     } elseif ($class->isAbstract()) {
                         $this->checkNaming($node->getLine(), $class->getShortName(), $class->getShortName(), 'abstract_class_name');
                     }
                     // No Else-If here as there might be an abstract exception.
                     // TODO: isSubtype needs to return a TernaryValue so that we
                     //       can decide here what to do in the unknown case.
                     //                        if ($class->isSubTypeOf($this->typeRegistry->getClassOrCreate('Exception'))) {
                     //                            $this->checkNaming($node->getLine(), $class->getShortName(), $class->getShortName(), 'exception_name');
                     //                        }
                 }
                 // The original check only allows final classes to have a private
                 // constructor. We also allow the constructor to be private if it
                 // is declared final, and the class is declared abstract. This is
                 // for example a common practice for utility classes.
                 $method = $class->getMethod('__construct');
                 if (null !== $method && $method->isPrivate() && !$class->isFinal() && (!$method->isFinal() || !$class->isAbstract())) {
                     if (($method = $class->getMethod('__construct')) && $method->isPrivate() && !$class->isFinal() && (!$method->isFinal() || !$class->isAbstract())) {
                         if ($method->isFinal()) {
                             if ($class->isUtilityClass()) {
                                 $this->phpFile->addComment($node->getLine(), Comment::warning('coding_style.non_abstract_util_class', 'Since you have declared the constructor as final, and this seems like a utility class, maybe you should also declare the class as abstract.'));
                             } else {
                                 if (null === $method->getAstNode()) {
                                     break;
                                 }
                                 $this->phpFile->addComment($method->getAstNode()->getLine(), Comment::warning('coding_style.non_util_class_with_final_private_constructor', 'Instead of declaring the constructor as final, maybe you should declare the entire class as final.'));
                             }
                         } elseif ($class->isAbstract()) {
                             if ($class->isUtilityClass()) {
                                 $this->phpFile->addComment($method->getAstNode()->getLine(), Comment::warning('coding_style.util_class_with_non_final_constructor', 'Since you have declared this class abstract, and the constructor is private, maybe you should also declare the constructor as final.'));
                             } else {
                                 $this->phpFile->addComment($method->getAstNode()->getLine(), Comment::warning('coding_style.abstract_non_util_class_with_private_constructor', 'Something seems to be off here. Are you sure you want to declare the constructor as private, and the class as abstract?'));
                             }
                         } else {
                             $this->phpFile->addComment($node->getLine(), Comment::warning('coding_style.non_final_class_with_private_constructor', 'Since you have declared the constructor as private, maybe you should also declare the class as final.'));
                         }
                     }
                 }
             }
             break;
     }
 }
 private function addToUseIfLocal(\PHPParser_Node $varNode, \PHPParser_Node $cfgNode, UseLattice $uses)
 {
     if (!NodeUtil::isName($varNode)) {
         return;
     }
     $name = $varNode->name;
     $var = $this->scope->getVar($name);
     if (null === $var) {
         return;
     }
     $uses->addUse($var, $cfgNode, $varNode);
 }
 /**
  * Declares a refined type in {@code scope} for the name represented by
  * {@code node}. It must be possible to refine the type of the given node in
  * the given scope, as determined by {@link #getTypeIfRefinable}.
  */
 protected function declareNameInScope(LinkedFlowScope $scope, \PHPParser_Node $node, PhpType $type)
 {
     switch (true) {
         case $node instanceof \PHPParser_Node_Expr_Variable:
             if (is_string($node->name)) {
                 $scope->inferSlotType($node->name, $type);
             }
             break;
         case $node instanceof \PHPParser_Node_Expr_StaticPropertyFetch:
         case $node instanceof \PHPParser_Node_Expr_PropertyFetch:
             if (null !== ($qualifiedName = TypeInference::getQualifiedName($node))) {
                 $origType = $node->getAttribute('type') ?: $this->typeRegistry->getNativeType('unknown');
                 $scope->inferQualifiedSlot($node, $qualifiedName, $origType, $type);
             }
             break;
             // Something like: if (is_array($functions = getFunctionNames())) { }
         // Something like: if (is_array($functions = getFunctionNames())) { }
         case $node instanceof \PHPParser_Node_Expr_Assign:
         case $node instanceof \PHPParser_Node_Expr_AssignRef:
             $this->declareNameInScope($scope, $node->var, $type);
             break;
         case $node instanceof \PHPParser_Node_Expr_ArrayDimFetch:
             $dim = \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::getValue($node->dim);
             if ($dim->isDefined() && null !== ($slotType = $node->var->getAttribute('type')) && $slotType->isArrayType()) {
                 $newSlotType = $slotType->inferItemType($dim->get(), $type);
                 $this->declareNameInScope($scope, $node->var, $newSlotType);
             }
             break;
     }
 }
 public function getPreciserFunctionReturnTypeKnowingArguments(GlobalFunction $function, array $argValues, array $argTypes)
 {
     switch ($function->getName()) {
         case 'version_compare':
             switch (count($argTypes)) {
                 case 2:
                     return $this->registry->getNativeType('integer');
                 case 3:
                     return $this->registry->getNativeType('boolean');
                 default:
                     return $this->registry->resolveType('integer|boolean');
             }
         case 'unserialize':
             return $this->registry->getNativeType('unknown_checked');
         case 'var_export':
             if (count($argValues) !== 2) {
                 return null;
             }
             if (\Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::isBoolean($argValues[1]) && \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::getBooleanValue($argValues[1]) === true) {
                 return $this->registry->getNativeType('string');
             }
             return null;
         case 'min':
         case 'max':
             switch (count($argTypes)) {
                 case 0:
                     return null;
                 case 1:
                     if ($argTypes[0]->isArrayType()) {
                         return $argTypes[0]->getElementType();
                     }
                     return null;
                 default:
                     // TODO: We could make this a bit smarter as some types are always considered
                     //       greater/smaller than other types.
                     //       See http://de1.php.net/manual/en/language.operators.comparison.php
                     return $this->registry->createUnionType($argTypes);
             }
         case 'str_replace':
         case 'preg_filter':
         case 'preg_replace':
         case 'preg_replace_callback':
             if (isset($argTypes[2])) {
                 if ($argTypes[2]->isUnknownType()) {
                     return $this->registry->getNativeType('unknown');
                 }
                 if ($argTypes[2]->isArrayType()) {
                     return $this->registry->resolveType('array<string>');
                 }
                 $nullableScalar = $this->registry->createNullableType($this->registry->getNativeType('scalar'));
                 if ($argTypes[2]->isSubtypeOf($nullableScalar)) {
                     return $this->registry->getNativeType('string');
                 }
             }
             return null;
             // Use the non-restricted type if we don't know.
         // Use the non-restricted type if we don't know.
         default:
             return null;
     }
 }
 private function checkBitwiseOperation()
 {
     if (!NodeUtil::isEqualityExpression($this->stream->node->left)) {
         return;
     }
     /*
      * When this method is called, the token stream points to the bitwise operation already.
      *
      * 0 === foo() & 5;
      *             ^
      * -----------(1)
      *
      * (1) The position of the token stream when this method is called.
      *
      * We now check that the equality expression on the left side (in the case above, it is an Identical node) ends
      * with a parenthesis, and the ending parenthesis is started somewhere on the left side side of the equality
      * operator (===).
      */
     /** @var AbstractToken $closingParenthesis */
     $closingParenthesis = $this->stream->token->findPreviousToken('NO_WHITESPACE_OR_COMMENT')->get();
     if ($closingParenthesis->matches(')')) {
         $startToken = $this->stream->node->left->left->getAttribute('start_token');
         // The left operator of the equality sign is not assigned an explicit start token, so we do not know where
         // it started. We can just bail out here, and hope that the closing parenthesis is indeed started on the left
         // side. This needs to be improved in the SimultaneousTokenAndAstStream over time so that we ideally always
         // have a start token available.
         if (null === $startToken) {
             return;
         }
         // The closing parenthesis was started somewhere on the left side, good !
         if ($closingParenthesis->isClosing($startToken->findPreviousToken('NO_WHITESPACE_OR_COMMENT')->get())) {
             return;
         }
     }
     $this->phpFile->addComment($this->stream->node->getLine(), Comment::warning('precedence.possibly_wrong_comparison_of_bitop_result', 'Consider adding parentheses for clarity. Current Interpretation: ``%current_compare%``, Probably Intended Meaning: ``%alternative_compare%``', array('current_compare' => '(' . self::$prettyPrinter->prettyPrintExpr($this->stream->node->left) . ') ' . NodeUtil::getBitOp($this->stream->node) . ' ' . self::$prettyPrinter->prettyPrintExpr($this->stream->node->right), 'alternative_compare' => self::$prettyPrinter->prettyPrintExpr($this->stream->node->left->left) . ' ' . NodeUtil::getEqualOp($this->stream->node->left) . ' (' . self::$prettyPrinter->prettyPrintExpr($this->stream->node->left->right) . ' ' . NodeUtil::getBitOp($this->stream->node) . ' ' . self::$prettyPrinter->prettyPrintExpr($this->stream->node->right) . ')')));
 }
 private function checkForMissingProperty(\PHPParser_Node_Expr_PropertyFetch $node)
 {
     if (!is_string($node->name)) {
         return;
     }
     if (!($objType = $node->var->getAttribute('type'))) {
         return;
     }
     $objType = $objType->restrictByNotNull()->toMaybeObjectType();
     if (!$objType) {
         // TODO: Add support to check on union types.
         return;
     }
     if ($objType->isInterface()) {
         $this->phpFile->addComment($node->getLine(), Comment::warning('types.property_access_on_interface', 'Accessing "%property_name%" on the interface "%interface_name%" suggest that you code against a concrete implementation. How about adding an ``instanceof`` check?', array('property_name' => $node->name, 'interface_name' => $objType->getName())));
         return;
     }
     if (!$objType->isNormalized() || $objType->hasProperty($node->name)) {
         return;
     }
     // Ignore all property accesses on ``stdClass`` objects. Currently, we have
     // no way to describe, or track RECORD types. As such, any messages related to
     // stdClass are likely wrong, but at least there are too many false-positives
     // for now. So, we just disable this.
     if ($objType->isSubtypeOf($this->typeRegistry->getClassOrCreate('stdClass'))) {
         return;
     }
     // Ignore all property reads on ``SimpleXMLElement`` objects. The reasoning
     // behind this is similar to the exception for ``stdClass`` objects above.
     // We simply have no reliable way to describe the structure of these objects
     // for now. So, we disable checks for them to avoid a flood of false-positives.
     if (!\Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::isAssignmentOp($node->getAttribute('parent')) && $objType->isSubtypeOf($this->typeRegistry->getClassOrCreate('SimpleXMLElement'))) {
         return;
     }
     // Property accesses inside isset() are safe, do not make any noise just yet.
     if ($node->getAttribute('parent') instanceof \PHPParser_Node_Expr_Isset) {
         return;
     }
     if (null !== ($bestName = $this->getMostSimilarName($node->name, $objType->getPropertyNames()))) {
         $this->phpFile->addComment($node->getLine(), Comment::error('typos.mispelled_property_name', 'The property "%offending_property_name%" does not exist. Did you mean "%closest_property_name%"?', array('offending_property_name' => $node->name, 'closest_property_name' => $bestName)));
     } else {
         // Ignore additional accesses to this property.
         $objType->addProperty(new Property($node->name));
         if (\Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::isAssignmentOp($node->getAttribute('parent'))) {
             if ($objType->hasMethod('__set')) {
                 $this->phpFile->addComment($node->getLine(), Comment::warning('strict.maybe_undocument_property_write_capability', 'The property ``%property_name%`` does not exist. Since you implemented ``__set``, maybe consider adding a [@property annotation](http://www.phpdoc.org/docs/latest/for-users/tags/property.html).', array('property_name' => $node->name)));
                 return;
             }
         } else {
             if ($objType->hasMethod('__get')) {
                 $this->phpFile->addComment($node->getLine(), Comment::warning('strict.maybe_undocument_property_read_capability', 'The property ``%property_name%`` does not exist. Since you implemented ``__get``, maybe consider adding a [@property annotation](http://www.phpdoc.org/docs/latest/for-users/tags/property.html).', array('property_name' => $node->name)));
                 return;
             }
         }
         $thisType = $this->t->getScope()->getTypeOfThis();
         if ($thisType && $thisType->equals($objType)) {
             $this->phpFile->addComment($node->getLine(), Comment::warning('strict.undeclared_property', 'The property "%property_name%" does not exist. Did you maybe forget to declare it?', array('property_name' => $node->name)));
         } else {
             $this->phpFile->addComment($node->getLine(), Comment::error('typos.non_existent_property', 'The property "%property_name%" does not exist.', array('property_name' => $node->name)));
         }
     }
 }
 private function assertNotMatch($code)
 {
     $this->computeUseDef($code);
     $analyzedUses = $this->analysis->getUses('x', $this->def);
     foreach ($this->uses as $use) {
         if (in_array($use, $analyzedUses, true)) {
             $this->fail('Did not expect ' . \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::getStringRepr($use));
         }
     }
 }
 public function checkForeach(\PHPParser_Node_Stmt_Foreach $node)
 {
     if ($this->getSetting('foreach.value_as_reference') && $node->byRef && !NodeUtil::canBePassedByRef($node->expr)) {
         $this->phpFile->addComment($node->expr->getLine(), Comment::error('usage_context.foreach_expr_no_reference', 'The expression ``%expr%`` cannot be used as a reference.', array('expr' => self::$prettyPrinter->prettyPrintExpr($node->expr))));
     }
     $this->checkTraversable($node);
 }
Exemplo n.º 28
0
 private function traverseScopeCreator(\PHPParser_Node $node, \PHPParser_Node $parent = null)
 {
     assert(NodeUtil::isScopeCreator($node));
     $this->curNode = $node;
     $isScopeActive = $this->getScopeRoot() === $node;
     if (!$isScopeActive) {
         $this->pushScopeWithRoot($node);
     }
     // args
     foreach ($node->params as $param) {
         $this->traverseBranch($param, $node);
     }
     // uses from parent scope
     if ($node instanceof \PHPParser_Node_Expr_Closure) {
         foreach ($node->uses as $use) {
             $this->traverseBranch($use, $node);
         }
     }
     // body
     // For abstract methods the stmts property is null, which we need to check here.
     if (null !== $node->stmts) {
         $this->traverseBranch($node->stmts, $node);
     }
     if (!$isScopeActive) {
         $this->popScope();
     }
 }
 private function inMethod($phpCode)
 {
     // Parse the body of the function.
     $ast = $this->parser->parse(new \PHPParser_Lexer('<?php class Foo { function foo() {' . $phpCode . '} }'));
     // Normalize the AST.
     $traverser = new \PHPParser_NodeTraverser();
     $traverser->addVisitor(new \PHPParser_NodeVisitor_NameResolver());
     $traverser->addvisitor(new NormalizingNodeVisitor());
     $ast = $traverser->traverse($ast);
     $traverser = new \PHPParser_NodeTraverser();
     $traverser->addVisitor(new \PHPParser_NodeVisitor_NodeConnector());
     $traverser->traverse($ast);
     $root = $ast[0];
     $node = $root->stmts[0]->stmts;
     // Create the scope with the assumptions.
     $scopeCreator = new TypedScopeCreator($this->registry);
     $assumedScope = $scopeCreator->createScope($node, $scopeCreator->createScope($root, null));
     foreach ($this->assumptions as $symbolName => $type) {
         $var = $assumedScope->getVar($symbolName);
         if (!$var) {
             $assumedScope->declareVar($symbolName, $type);
         } else {
             $var->setType($type);
         }
     }
     // Create the control graph.
     $cfa = new ControlFlowAnalysis();
     $cfa->process($node);
     $cfg = $cfa->getGraph();
     // Create a simple reverse abstract interpreter.
     $rai = new SemanticReverseAbstractInterpreter($this->registry);
     $fi = new ArrayFunctionInterpreter($this->registry);
     $mi = $this->getMock('Scrutinizer\\PhpAnalyzer\\DataFlow\\TypeInference\\MethodInterpreter\\MethodInterpreterInterface');
     $commentParser = new \Scrutinizer\PhpAnalyzer\PhpParser\DocCommentParser($this->registry);
     // Do the type inference by data-flow analysis.
     $dfa = new TypeInference($cfg, $rai, $fi, $mi, $commentParser, $assumedScope, $this->registry);
     $dfa->analyze();
     // Get the scope of the implicit return.
     $this->returnScope = $cfg->getImplicitReturn()->getAttribute(DataFlowAnalysis::ATTR_FLOW_STATE_IN);
     $this->astGraph = \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::dump($node);
 }