/**
  * Report coding standard violation if class/interface/trait/function is too big
  *
  * @param PHPParser_Node $node Current node
  *
  * {@internal 'maximum' is padded to compensate for opening/closing curly braces }}
  */
 private function checkSize($node)
 {
     $keyword = $this->metaData[$node->getType()]['keyword'];
     $maximum = $this->metaData[$node->getType()]['maximum'];
     $size = $node->getAttribute('endLine') - $node->getAttribute('startLine');
     if ($size <= $maximum + 2) {
         return;
     }
     $this->phpcsFile->addWarning(sprintf('Keep your %s small (no more than %d lines)', $keyword, $maximum), $this->findStackPointer($node));
 }
 /**
  * @depends testConstruct
  */
 public function testGetDocComment(PHPParser_Node $node)
 {
     $this->assertEquals('/** doc comment */', $node->getDocComment());
     array_pop($node->getAttribute('comments'));
     // remove doc comment
     $this->assertNull($node->getDocComment());
     array_pop($node->getAttribute('comments'));
     // remove comment
     $this->assertNull($node->getDocComment());
 }
示例#3
0
 public function getNodeCode(\PHPParser_Node $node)
 {
     $startPos = $node->getAttribute('startOffset');
     $endPos = $node->getAttribute('endOffset');
     if ($startPos === null || $endPos === null) {
         return '';
         // just to be sure
     }
     $startOffset = $this->tokenToStartOffset[$startPos];
     $endOffset = $this->tokenToEndOffset[$endPos];
     return substr($this->code, $startOffset, $endOffset - $startOffset);
 }
 public function enterNode(\PHPParser_Node $node)
 {
     // Determine info about the closure's location
     if (!$this->closureNode) {
         if ($node instanceof \PHPParser_Node_Stmt_Namespace) {
             $this->location->namespace = is_array($node->name->parts) ? implode('\\', $node->name->parts) : null;
         }
         if ($node instanceof \PHPParser_Node_Stmt_Trait) {
             $this->location->trait = $this->location->namespace . '\\' . $node->name;
             $this->location->class = null;
         } elseif ($node instanceof \PHPParser_Node_Stmt_Class) {
             $this->location->class = $this->location->namespace . '\\' . $node->name;
             $this->location->trait = null;
         }
     }
     // Locate the node of the closure
     if ($node instanceof \PHPParser_Node_Expr_Closure) {
         if ($node->getAttribute('startLine') == $this->reflection->getStartLine()) {
             if ($this->closureNode) {
                 throw new \RuntimeException('Two closures were declared on the same line of code. Cannot determine ' . 'which closure was the intended target.');
             } else {
                 $this->closureNode = $node;
             }
         }
     }
 }
 /**
  * {@inheritdoc}
  */
 public function enterNode(PHPParser_Node $node)
 {
     if ($node->getAttribute('visited')) {
         return;
     }
     return;
     switch ($node->getType()) {
         case 'Stmt_ClassMethod':
         case 'Expr_Closure':
         case 'Stmt_Function':
         case 'Stmt_If':
         case 'Stmt_Do':
         case 'Stmt_While':
         case 'Stmt_For':
         case 'Stmt_Foreach':
             break;
     }
 }
 public function leaveNode(\PHPParser_Node $node)
 {
     switch ($node->getType()) {
         case 'Scalar_MagicConst_Line':
             return new NumberNode($node->getAttribute('startLine'));
         case 'Scalar_MagicConst_File':
             return new StringNode($this->location->file);
         case 'Scalar_MagicConst_Dir':
             return new StringNode($this->location->directory);
         case 'Scalar_MagicConst_Function':
             return new StringNode($this->location->function);
         case 'Scalar_MagicConst_Namespace':
             return new StringNode($this->location->namespace);
         case 'Scalar_MagicConst_Class':
             return new StringNode($this->location->class);
         case 'Scalar_MagicConst_Method':
             return new StringNode($this->location->method);
         case 'Scalar_MagicConst_Trait':
             return new StringNode($this->location->trait);
     }
 }
 private function attachLiteralTypes(\PHPParser_Node $node, \PHPParser_Node $parent = null)
 {
     switch (true) {
         case $node instanceof \PHPParser_Node_Name_FullyQualified:
             $node->setAttribute('type', $this->typeRegistry->getClassOrCreate(implode("\\", $node->parts)));
             break;
         case $node instanceof \PHPParser_Node_Name:
             if ($parent instanceof \PHPParser_Node_Expr_New || $parent instanceof \PHPParser_Node_Expr_StaticPropertyFetch || $parent instanceof \PHPParser_Node_Expr_StaticCall || $parent instanceof \PHPParser_Node_Expr_Instanceof || $parent instanceof \PHPParser_Node_Stmt_Catch) {
                 $name = implode("\\", $node->parts);
                 $lowerName = strtolower($name);
                 if ('static' === $name) {
                     $node->setAttribute('type', $this->typeRegistry->getThisType($this->scope->getTypeOfThis()));
                 } else {
                     if ('self' === $name) {
                         $node->setAttribute('type', $this->scope->getTypeOfThis());
                     } else {
                         if ('parent' === $name) {
                             $thisType = $this->scope->getTypeOfThis()->toMaybeObjectType();
                             if (null === $thisType || !$thisType->isClass() || null === $thisType->getSuperClassType()) {
                                 $node->setAttribute('type', $this->typeRegistry->getNativeType('unknown'));
                             } else {
                                 $node->setAttribute('type', $thisType->getSuperClassType());
                             }
                         } else {
                             $node->setAttribute('type', $this->typeRegistry->getClassOrCreate($name));
                         }
                     }
                 }
             }
             break;
         case $node instanceof \PHPParser_Node_Expr_Array:
         case $node instanceof \PHPParser_Node_Expr_Cast_Array:
             // We only do attach the generic array type on the first build.
             // For subsequent builds, other passes likely have already made
             // the array type more specific.
             if (null === $node->getAttribute('type')) {
                 $node->setAttribute('type', $this->typeRegistry->getNativeType('array'));
             }
             break;
         case $node instanceof \PHPParser_Node_Expr_UnaryMinus:
         case $node instanceof \PHPParser_Node_Expr_UnaryPlus:
         case $node instanceof \PHPParser_Node_Scalar_LNumber:
         case $node instanceof \PHPParser_Node_Scalar_LineConst:
             $node->setAttribute('type', $this->typeRegistry->getNativeType('integer'));
             break;
         case $node instanceof \PHPParser_Node_Scalar_DNumber:
             $node->setAttribute('type', $this->typeRegistry->getNativeType('double'));
             break;
         case $node instanceof \PHPParser_Node_Scalar_String:
         case $node instanceof \PHPParser_Node_Scalar_FileConst:
         case $node instanceof \PHPParser_Node_Scalar_DirConst:
             $node->setAttribute('type', $this->typeRegistry->getNativeType('string'));
             break;
         case $node instanceof \PHPParser_Node_Expr_ClassConstFetch:
             if ($node->class instanceof \PHPParser_Node_Name && in_array($node->class->parts[0], array('self', 'static')) && null !== ($thisType = $this->scope->getTypeOfThis()) && null !== ($objType = $thisType->toMaybeObjectType()) && ($objType->isClass() || $objType->isInterface()) && $objType->hasConstant($node->name)) {
                 $node->setAttribute('type', $objType->getConstant($node->name)->getPhpType());
             }
             break;
         case $node instanceof \PHPParser_Node_Expr_ConstFetch:
             $nameParts = $node->name->parts;
             if (1 === count($nameParts)) {
                 $name = strtolower($nameParts[0]);
                 if ('true' === $name) {
                     $node->setAttribute('type', $this->typeRegistry->getNativeType('boolean'));
                 } else {
                     if ('false' === $name) {
                         $node->setAttribute('type', $this->typeRegistry->getNativeType('false'));
                     } else {
                         if ('null' === $name) {
                             $node->setAttribute('type', $this->typeRegistry->getNativeType('null'));
                         }
                     }
                 }
             }
             break;
     }
 }
 /**
  * Returns the class a method was called on.
  *
  * @param \PHPParser_Node $node
  *
  * @return null|PhpType
  */
 public function getCalledClassByNode(\PHPParser_Node $node)
 {
     switch (true) {
         case $node instanceof \PHPParser_Node_Expr_MethodCall:
             if (null === ($objType = $node->var->getAttribute('type'))) {
                 return null;
             }
             return $objType->restrictByNotNull()->toMaybeObjectType();
         case $node instanceof \PHPParser_Node_Expr_StaticCall:
             if (null === ($objType = $node->class->getAttribute('type'))) {
                 return null;
             }
             return $objType->restrictByNotNull()->toMaybeObjectType();
         case $node instanceof \PHPParser_Node_Expr_New:
             if (null === ($objType = $node->getAttribute('type'))) {
                 return null;
             }
             return $objType->restrictByNotNull()->toMaybeObjectType();
         default:
             throw new \InvalidArgumentException('The node class "' . get_class($node) . '" is not resolvable to a function/method.');
     }
 }
 /**
  * Returns the type of the given node in the flow scope.
  *
  * If the node cannot be matched to a slot, or simply is not capable of being
  * refined, then this method returns null.
  *
  * @return PhpType|null
  */
 public function getTypeIfRefinable(\PHPParser_Node $node, LinkedFlowScope $scope)
 {
     switch (true) {
         case $node instanceof \PHPParser_Node_Expr_ArrayDimFetch:
             $varType = $this->getTypeIfRefinable($node->var, $scope);
             if ($varType && $varType->isArrayType()) {
                 $dim = \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::getValue($node->dim);
                 if ($dim->isEmpty()) {
                     return null;
                 }
                 return $dim->flatMap([$varType, 'getItemType'])->getOrCall([$varType, 'getElementType']);
             }
             return null;
             // Handle the common pattern of assigning the result of an expression
             // inside of a condition, e.g. ``if (null !== $obj = $this->findObj())``.
         // Handle the common pattern of assigning the result of an expression
         // inside of a condition, e.g. ``if (null !== $obj = $this->findObj())``.
         case $node instanceof \PHPParser_Node_Expr_Assign:
         case $node instanceof \PHPParser_Node_Expr_AssignRef:
             if (TypeInference::hasQualifiedName($node->var) && null !== ($qualifiedName = TypeInference::getQualifiedName($node->var)) && null !== ($nameVar = $scope->getSlot($node->var->name))) {
                 if (null !== ($nameType = $nameVar->getType())) {
                     return $nameType;
                 }
                 return $node->var->getAttribute('type');
             }
             return null;
         case $node instanceof \PHPParser_Node_Expr_Variable:
             if (is_string($node->name)) {
                 $nameVar = $scope->getSlot($node->name);
                 if (null !== $nameVar) {
                     if (null !== $nameVar->getType()) {
                         return $nameVar->getType();
                     }
                     return $node->getAttribute('type');
                 }
             }
             return null;
         case $node instanceof \PHPParser_Node_Expr_StaticPropertyFetch:
         case $node instanceof \PHPParser_Node_Expr_PropertyFetch:
             if (null === ($qualifiedName = TypeInference::getQualifiedName($node))) {
                 return null;
             }
             $propVar = $scope->getSlot($qualifiedName);
             if (null !== $propVar && null !== $propVar->getType()) {
                 return $propVar->getType();
             }
             if (null !== ($type = $node->getAttribute('type'))) {
                 return $type;
             }
             return null;
     }
     return null;
 }
 private function handleCall(\PHPParser_Node $node)
 {
     if (!$this->getSetting('assignment_of_null_return')) {
         return;
     }
     if (!($returnType = $node->getAttribute('type'))) {
         return;
     }
     if (!$returnType->isNullType()) {
         return;
     }
     if (!$node->getAttribute('parent') instanceof \PHPParser_Node_Expr_Assign) {
         return;
     }
     $this->phpFile->addComment($node->getAttribute('parent')->getLine(), Comment::warning('suspicious_code.assignment_of_null_return', 'The assignment to ``%assignment_expr%`` looks wrong as ``%call_expr%`` always returns null.', array('assignment_expr' => self::$prettyPrinter->prettyPrintExpr($node->getAttribute('parent')->var), 'call_expr' => self::$prettyPrinter->prettyPrintExpr($node))));
 }
 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;
 }
 private function findContinueOrBreakTarget(\PHPParser_Node $node)
 {
     if (null !== $node->num && !$node->num instanceof \PHPParser_Node_Scalar_LNumber) {
         return null;
     }
     // Continuously look up the ancestor tree for the BREAK target, and connect to it.
     $curNb = 0;
     $num = null === $node->num ? 1 : $node->num->value;
     if (0 === $num) {
         $num = 1;
     }
     $parent = $node->getAttribute('parent');
     while (null !== $parent) {
         if ($parent instanceof \PHPParser_Node_Stmt_For || $parent instanceof \PHPParser_Node_Stmt_Foreach || $parent instanceof \PHPParser_Node_Stmt_While || $parent instanceof \PHPParser_Node_Stmt_Do || $parent instanceof \PHPParser_Node_Stmt_Switch) {
             $curNb += 1;
             if ($curNb >= $num) {
                 break;
             }
         }
         $parent = $parent->getAttribute('parent');
     }
     return $parent;
 }
 /**
  * This method gets the PhpType from the Node argument and verifies that it is
  * present. If not type is present, it will be logged, and unknown is returned.
  *
  * @return PhpType
  */
 private function getType(\PHPParser_Node $node)
 {
     $type = $node->getAttribute('type');
     if (null === $type) {
         // Theoretically, we should never enter this branch because all
         // interesting nodes should have received a type by the scope builder,
         // or the type inference engine. It is not worth throwing an exception,
         // but we should at least log it, and fix it over time.
         $this->logger->debug(sprintf('Encountered untyped node "%s"; assuming unknown type.', get_class($node)));
         return $this->typeRegistry->getNativeType('unknown');
     }
     return $type;
 }
 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;
         }
     }
 }
示例#15
0
 /**
  * Returns the parent node of the given class, or null if not found.
  *
  * @param \PHPParser_Node $node
  * @param string $parentClass
  *
  * @return \PHPParser_Node|null
  */
 public static function findParent(\PHPParser_Node $node, $parentClass)
 {
     $parent = $node->getAttribute('parent');
     while (null !== $parent) {
         if ($parent instanceof $parentClass) {
             return $parent;
         }
         $parent = $parent->getAttribute('parent');
     }
     return null;
 }
 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);
                     }
                 }
             }
     }
 }