public function enterScope(NodeTraversal $traversal)
 {
     // This has the side-effect of attaching type information to parameters.
     // TODO: This is more a hack atm, we should look into refactoring this.
     //       Maybe a simple rename to buildScope(), or getOrBuildScope.
     $traversal->getScope();
 }
 public function testTraverse()
 {
     $function = new \PHPParser_Node_Stmt_Function('foo', array('stmts' => $functionBlock = new BlockNode(array($try = new \PHPParser_Node_Stmt_TryCatch(array(), array($catch1 = new \PHPParser_Node_Stmt_Catch(new \PHPParser_Node_Name(array('Foo')), 'ex'), $catch2 = new \PHPParser_Node_Stmt_Catch(new \PHPParser_Node_Name(array('Bar')), 'ex'))), $echo = new \PHPParser_Node_Stmt_Echo(array(new \PHPParser_Node_Scalar_String('foo')))))));
     $tryBlock = $try->stmts = new BlockNode(array($throw = new \PHPParser_Node_Stmt_Throw(new \PHPParser_Node_Expr_Variable('foo'))));
     $catch1Block = $catch1->stmts = new BlockNode(array());
     $catch2Block = $catch2->stmts = new BlockNode(array());
     $callback = $this->getMock('Scrutinizer\\PhpAnalyzer\\PhpParser\\Traversal\\CallbackInterface');
     $t = new NodeTraversal($callback);
     $callback->expects($this->at(0))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $function, null)->will($this->returnValue(true));
     $callback->expects($this->at(1))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $functionBlock, $function)->will($this->returnValue(true));
     $callback->expects($this->at(2))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $try, $functionBlock)->will($this->returnValue(true));
     $callback->expects($this->at(3))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $tryBlock, $try)->will($this->returnValue(true));
     $callback->expects($this->at(4))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $throw, $tryBlock)->will($this->returnValue(false));
     $callback->expects($this->at(5))->method('visit')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $tryBlock, $try);
     $callback->expects($this->at(6))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $catch1, $try)->will($this->returnValue(true));
     $callback->expects($this->at(7))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $this->isInstanceOf('PHPParser_Node_Name'), $catch1)->will($this->returnValue(false));
     $callback->expects($this->at(8))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $catch1Block, $catch1)->will($this->returnValue(true));
     $callback->expects($this->at(9))->method('visit')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $catch1Block, $catch1);
     $callback->expects($this->at(10))->method('visit')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $catch1, $try);
     $callback->expects($this->at(11))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $catch2, $try)->will($this->returnValue(false));
     $callback->expects($this->at(12))->method('visit')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $try, $functionBlock);
     $callback->expects($this->at(13))->method('shouldTraverse')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $echo, $functionBlock)->will($this->returnValue(false));
     $callback->expects($this->at(14))->method('visit')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $functionBlock, $function);
     $callback->expects($this->at(15))->method('visit')->with($this->isInstanceOf('Scrutinizer\\PhpAnalyzer\\PhpParser\\NodeTraversal'), $function, null);
     $t->traverse($function);
 }
 public function enterScope(NodeTraversal $t)
 {
     $node = $t->getScopeRoot();
     $function = null;
     if ($node instanceof \PHPParser_Node_Stmt_Function) {
         $function = $this->typeRegistry->getFunction($node->name);
         if (null !== $function) {
             $this->parser->setCurrentClassName(null);
             $this->parser->setImportedNamespaces($this->importedNamespaces = $function->getImportedNamespaces());
         }
     } else {
         if ($node instanceof \PHPParser_Node_Stmt_ClassMethod) {
             $objType = $t->getScope()->getTypeOfThis()->toMaybeObjectType();
             if (null !== $objType) {
                 /** @var $objType MethodContainer */
                 $this->parser->setCurrentClassName($objType->getName());
                 $this->parser->setImportedNamespaces($this->importedNamespaces = $objType->getImportedNamespaces());
                 $function = $objType->getMethod($node->name);
             }
         }
     }
     if (null !== $function) {
         if (null !== ($returnType = $function->getReturnType())) {
             $this->verifyReturnType($returnType, $node);
         }
         foreach ($function->getParameters() as $param) {
             /** @var $param Parameter */
             if (null !== ($paramType = $param->getPhpType())) {
                 $this->verifyParamType($paramType, $node, $param->getName());
             }
         }
     }
     return true;
 }
 public function enterScope(NodeTraversal $t)
 {
     $scope = $t->getScope();
     $cfg = $t->getControlFlowGraph();
     $this->liveness = new LiveVariablesAnalysis($cfg, $scope);
     $this->liveness->analyze();
     $this->tryRemoveDeadAssignments($t, $cfg);
 }
 public function enterScope(NodeTraversal $t)
 {
     $cfg = $t->getControlFlowGraph();
     $scope = $t->getScope();
     $mayBeAnalysis = new MayBeReachingUseAnalysis($cfg, $scope);
     $mayBeAnalysis->analyze();
     $mustBeAnalysis = new MustBeReachingDefAnalysis($cfg, $scope);
     $mustBeAnalysis->setLogger($this->logger);
     $mustBeAnalysis->analyze();
 }
 public function enterScope(NodeTraversal $traversal)
 {
     $typeInference = new TypeInference($traversal->getControlFlowGraph(), $this->reverseInterpreter, $this->functionInterpreter, $this->methodInterpreter, $this->commentParser, $traversal->getScope(), $this->registry, $this->logger);
     try {
         $typeInference->analyze();
     } catch (MaxIterationsExceededException $ex) {
         $scopeRoot = $traversal->getScopeRoot();
         $this->logger->warning($ex->getMessage() . ' - Scope-Root: ' . get_class($scopeRoot) . ' on line ' . $scopeRoot->getLine() . ' in ' . $this->file->getName());
     }
 }
 private function checkUnusedMethod(NodeTraversal $t, \PHPParser_Node_Stmt_ClassMethod $node)
 {
     if ((\PHPParser_Node_Stmt_Class::MODIFIER_PRIVATE & $node->type) === 0) {
         return;
     }
     $classType = $t->getScope()->getTypeOfThis()->restrictByNotNull();
     if (($method = $classType->toMaybeObjectType()->getMethod($node->name)) && 0 === count($method->getInMethodCallSites())) {
         $this->phpFile->addComment($node->getLine(), Comment::warning('cleanup.unused_method', 'This method is unused, and could be removed.'));
     }
 }
 public function enterScope(NodeTraversal $t)
 {
     $node = $t->getScopeRoot();
     $function = null;
     if ($node instanceof \PHPParser_Node_Stmt_ClassMethod) {
         $function = $t->getScope()->getTypeOfThis()->getMethod($node->name)->getMethod();
     } else {
         if ($node instanceof \PHPParser_Node_Stmt_Function) {
             $function = $this->typeRegistry->getFunctionByNode($node);
         }
     }
     if (null !== $function) {
         $this->inferTypesForFunction($function);
     }
 }
 public function enterScope(NodeTraversal $t)
 {
     $root = $t->getScopeRoot();
     if (!$root instanceof \PHPParser_Node_Stmt_ClassMethod) {
         return;
     }
     if ($root->name !== $this->method) {
         return;
     }
     $container = $root->getAttribute('parent')->getAttribute('parent');
     $className = implode("\\", $container->namespacedName->parts);
     if ($this->class !== substr($className, -1 * strlen($this->class))) {
         return;
     }
     $this->cfg = $t->getControlFlowGraph();
 }
 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))));
     }
 }
 public function process(\PHPParser_Node $root)
 {
     $this->root = $root;
     $this->astPositionCounter = 0;
     $this->exceptionHandlers = new \SplStack();
     $this->graph = new ControlFlowGraph(self::computeFallThrough($root));
     NodeTraversal::traverseWithCallback($root, $this);
     $this->assignPriorities();
 }
Exemplo n.º 12
0
 private function updateAst()
 {
     try {
         $this->ast = ParseUtils::parse($this->getContent());
         NodeTraversal::traverseWithCallback($this->ast, $this->typeInferencePass, $this->typeInferencePass->getScopeCreator());
         NodeTraversal::traverseWithCallback($this->ast, $this->typeInferencePass, $this->typeInferencePass->getScopeCreator());
     } catch (\PHPParser_Error $ex) {
         // The original content of the file is not valid PHP code. We just ignore this.
         // Passes that require an AST, have to check with hasAst() manually.
     }
 }
 public function analyze(File $file)
 {
     if (!$this->isEnabled()) {
         return;
     }
     if (!$file instanceof PhpFile) {
         return;
     }
     $this->phpFile = $file;
     NodeTraversal::traverseWithCallback($file->getAst(), $this, $this->getScopeCreator());
 }
 public function shouldTraverse(NodeTraversal $t, \PHPParser_Node $node, \PHPParser_Node $parent = null)
 {
     $graphNode = $t->getControlFlowGraph()->getNode($node);
     if (null !== $graphNode && GraphReachability::UNREACHABLE === $graphNode->getAttribute(GraphReachability::ATTR_REACHABILITY)) {
         // Only report error when there are some line number informations.
         // There are synthetic nodes with no line number informations, nodes
         // introduced by other passes (although not likely since this pass should
         // be executed early) or some PHPParser bug.
         if (-1 !== $node->getLine()) {
             $this->file->addComment($node->getLine(), Comment::warning('usage.unreachable_code', '``%unreachable_code%`` is not reachable.', array('unreachable_code' => $this->prettyPrinter->prettyPrint(array($node)))));
             // From now on, we are going to assume the user fixed the error and not
             // give more warnings related to code sections reachable from this node.
             $r = new GraphReachability($t->getControlFlowGraph());
             $r->recompute($node);
             // Saves time by not traversing children.
             return false;
         }
     }
     return true;
 }
 public function createScope(\PHPParser_Node $node, Scope $parent = null)
 {
     $thisType = $this->getThisType($node, $parent);
     // Constructing the global scope is very different than constructing inner scopes, because only global scopes
     // can contain named classes that show up in the type registry.
     $newScope = null;
     if (null === $parent) {
         $newScope = new Scope($node, null, $thisType);
         $builder = new GlobalScopeBuilder($newScope, $this->typeRegistry);
         NodeTraversal::traverseWithCallback($node, $builder);
     } else {
         $newScope = new Scope($node, $parent, $thisType);
         $builder = new LocalScopeBuilder($newScope, $this->typeRegistry);
         NodeTraversal::traverseWithCallback($node, $builder);
     }
     if (null !== $thisType) {
         $newScope->declareVar('this', $thisType);
     }
     return $newScope;
 }
 private function computeUseDef($code)
 {
     $code = sprintf('<?php function foo($param1, $param2) { %s }', $code);
     $ast = \JMS\PhpManipulator\PhpParser\ParseUtils::parse($code);
     $scope = (new \Scrutinizer\PhpAnalyzer\PhpParser\Scope\SyntacticScopeCreator())->createScope($ast);
     $cfa = new \Scrutinizer\PhpAnalyzer\ControlFlow\ControlFlowAnalysis();
     $cfa->process($ast);
     $cfg = $cfa->getGraph();
     $this->analysis = new \Scrutinizer\PhpAnalyzer\DataFlow\VariableReachability\MayBeReachingUseAnalysis($cfg, $scope);
     $this->analysis->analyze();
     $this->def = null;
     $this->uses = array();
     \Scrutinizer\PhpAnalyzer\PhpParser\NodeTraversal::traverseWithCallback($ast, new \Scrutinizer\PhpAnalyzer\PhpParser\PreOrderCallback(function ($t, \PHPParser_Node $node) {
         if (!$node instanceof \PHPParser_Node_Stmt_Label) {
             return;
         }
         if ('D' === $node->name) {
             $this->def = $node->getAttribute('next');
         } else {
             if (0 === strpos($node->name, 'U')) {
                 $this->uses[] = $node->getAttribute('next');
             }
         }
     }));
     $this->assertNotNull($this->def);
     $this->assertNotEmpty($this->uses);
 }
Exemplo n.º 17
0
 /**
  * Returns the originating function, or method.
  *
  * @param NodeTraversal $t
  *
  * @return AbstractFunction|null
  */
 private function getSource(NodeTraversal $t)
 {
     $root = $t->getScopeRoot();
     if ($root instanceof \PHPParser_Node_Stmt_Function) {
         return $this->typeRegistry->getFunctionByNode($root);
     } else {
         if ($root instanceof \PHPParser_Node_Stmt_ClassMethod) {
             // If the originating object was not part of this packages' dependencies, or we
             // have not scanned it for some other reason, we have to bail out here.
             if (null === ($thisObject = $t->getScope()->getTypeOfThis()->toMaybeObjectType())) {
                 return null;
             }
             // This could be the case if the same class has been defined more than once.
             // We had such cases for example when people add fixtures for code generation to their
             // packages, and do not ensure that class names are unique.
             if (null === ($classMethod = $thisObject->getMethod($root->name))) {
                 return null;
             }
             return $classMethod->getMethod();
         }
     }
     return null;
 }
 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 handleAssign(NodeTraversal $t, \PHPParser_Node_Expr_Assign $node)
 {
     if (!$node->var instanceof \PHPParser_Node_Expr_Variable || !is_string($node->var->name)) {
         return;
     }
     $scope = $t->getScope();
     if (null === ($var = $scope->getVar($node->var->name))) {
         return;
     }
     if ($var->isReference()) {
         return;
     }
     $nameNode = $var->getNameNode();
     if ($nameNode instanceof \PHPParser_Node_Param && $this->getSetting('overriding_parameter')) {
         $this->phpFile->addComment($node->getLine(), Comment::warning('suspicious_code.assignment_to_parameter', 'Consider using a different name than the parameter ``$%param_name%``. This often makes code more readable.', array('param_name' => $node->var->name)));
     } else {
         if ($nameNode instanceof \PHPParser_Node_Expr_ClosureUse && $this->getSetting('overriding_closure_use')) {
             $this->phpFile->addComment($node->getLine(), Comment::warning('suspicious_code.assignment_to_closure_import', 'Consider using a different name than the imported variable ``$%variable_name%``, or did you forget to import by reference?', array('variable_name' => $node->var->name)));
         }
     }
 }
 private function checkCall(NodeTraversal $t, \PHPParser_Node $node)
 {
     if (!$node instanceof \PHPParser_Node_Expr_StaticCall && !$node instanceof \PHPParser_Node_Expr_MethodCall) {
         return;
     }
     $method = $this->typeRegistry->getCalledFunctionByNode($node);
     if (!$method instanceof ClassMethod) {
         return;
     }
     $type = $method->getClass();
     if ($method->isPublic()) {
         return;
     }
     $thisType = $t->getScope()->getTypeOfThis();
     if ($method->isPrivate()) {
         $type = $method->getDeclaringClassType();
         if (null === $thisType || !$thisType->equals($type)) {
             $this->phpFile->addComment($node->getLine(), $this->createInAccessibleMethodError($method->getMethod()->getName(), $type->getName(), 'private'));
             return;
         }
         // Check that ``static`` is not used with private methods.
         if ($node instanceof \PHPParser_Node_Expr_StaticCall && $node->class instanceof \PHPParser_Node_Name && 1 === count($node->class->parts) && 'static' === strtolower($node->class->parts[0])) {
             $this->phpFile->addComment($node->getLine(), Comment::error('access_control.static_with_private_method', 'Since ``%method_name%()`` is declared private, calling it with ``static`` will lead to errors in possible sub-classes. You can either use ``self``, or increase the visibility of ``%method_name%()`` to at least protected.', array('method_name' => $method->getMethod()->getName())));
         }
         return;
     }
     if ($method->isProtected()) {
         if (null === $thisType || !$thisType->isSubTypeOf($type)) {
             $this->phpFile->addComment($node->getLine(), $this->createInAccessibleMethodError($method->getMethod()->getName(), $type->getName(), 'protected'));
         }
         return;
     }
     throw new \LogicException('Invalid state.');
 }
 public function enterScope(NodeTraversal $t)
 {
     $scope = $t->getScope();
     $root = $scope->getRootNode();
     if (!$root instanceof \PHPParser_Node_Stmt_Function && !$root instanceof \PHPParser_Node_Stmt_ClassMethod) {
         return;
     }
     // Bail out on abstract methods.
     if ($root instanceof \PHPParser_Node_Stmt_ClassMethod && ($root->type & \PHPParser_Node_Stmt_Class::MODIFIER_ABSTRACT) !== 0) {
         return;
     }
     // Bail out on methods defined on interfaces.
     if ($root instanceof \PHPParser_Node_Stmt_ClassMethod && $root->getAttribute('parent')->getAttribute('parent') instanceof \PHPParser_Node_Stmt_Interface) {
         return;
     }
     // Bail out on built-in functions marked by the @jms-builtin annotation.
     // For these, we will solely infer types from doc comments.
     if (false !== strpos($root->getDocComment(), '@jms-builtin')) {
         return;
     }
     // Same as above, but for methods of classes marked with @jms-builtin.
     if ($root instanceof \PHPParser_Node_Stmt_ClassMethod) {
         $maybeClass = $root->getAttribute('parent')->getAttribute('parent');
         if ($maybeClass instanceof \PHPParser_Node_Stmt_Class && false !== strpos($maybeClass->getDocComment(), '@jms-builtin')) {
             return;
         }
     }
     $cfg = $t->getControlFlowGraph();
     $builder = new UnionTypeBuilder($this->typeRegistry);
     foreach ($cfg->getNode(null)->getInEdges() as $edge) {
         $sourceNode = $edge->getSource()->getAstNode();
         if (!$sourceNode instanceof \PHPParser_Node_Stmt_Return) {
             $builder->addAlternate($this->typeRegistry->getNativeType('null'));
             continue;
         }
         // If there is no type information available, we cannot make any
         // assumptions for this function/method.
         if (!($type = $sourceNode->getAttribute('type'))) {
             return;
         }
         $builder->addAlternate($type);
     }
     $type = $builder->build();
     if ($type->isUnknownType()) {
         return;
     }
     $function = $this->typeRegistry->getFunctionByNode($root);
     if ($function instanceof GlobalFunction) {
         if ($this->hasTypeChanged($type, $function->getReturnType())) {
             $this->repeatedPass->repeat();
         }
         $function->setReturnType($type);
     } else {
         if ($function instanceof ContainerMethodInterface) {
             $method = $function->getMethod();
             if ($this->hasTypeChanged($type, $method->getReturnType())) {
                 $this->repeatedPass->repeat();
             }
             $method->setReturnType($type);
         }
     }
 }
 private function computeDefUse($code)
 {
     $code = sprintf('<?php function foo($param1, $param2) { %s }', $code);
     $ast = ParseUtils::parse($code);
     $scope = (new SyntacticScopeCreator())->createScope($ast);
     $cfa = new ControlFlowAnalysis();
     $cfa->process($ast);
     $cfg = $cfa->getGraph();
     $this->analysis = new MustBeReachingDefAnalysis($cfg, $scope);
     $this->analysis->analyze();
     $this->def = $this->use = null;
     NodeTraversal::traverseWithCallback($ast, new PreOrderCallback(function (NodeTraversal $t, \PHPParser_Node $node) {
         if (!$node instanceof \PHPParser_Node_Stmt_Label) {
             return;
         }
         if ('D' === $node->name) {
             $this->def = $node->getAttribute('next');
         } else {
             if ('U' === $node->name) {
                 $this->use = $node->getAttribute('next');
             } else {
                 if (0 === strpos($node->name, 'DP_')) {
                     $className = 'PHPParser_Node_' . substr($node->name, 3);
                     $parent = $node;
                     while (null !== ($parent = $parent->getAttribute('parent'))) {
                         if ($parent instanceof $className) {
                             $this->def = $parent;
                             return;
                         }
                     }
                     throw new \RuntimeException(sprintf('Did not find any parent of class "%s".', $className));
                 }
             }
         }
     }));
     $this->assertNotNull($this->def, 'Did not find "D" (definition) label in the code.');
     $this->assertNotNull($this->use, 'Did not find "U" (usage) label in the code.');
 }