use Mfn\PHP\Analyzer\Analyzers\ObjectGraph\ObjectGraph;
use Mfn\PHP\Analyzer\Analyzers\Parser;
use Mfn\PHP\Analyzer\Logger\Stdout;
use Mfn\PHP\Analyzer\Project;
use Mfn\PHP\Analyzer\Util\Util;
use PhpParser\Lexer;
require_once __DIR__ . '/../vendor/autoload.php';
error_reporting(E_ALL);
Util::installMinimalError2ExceptionHandler();
$projectRealPath = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..');
# Use analyzer to gather the object graph
$project = new Project(new Stdout());
$project->addSplFileInfos(Util::scanDir(__DIR__ . '/../lib/'));
$project->addAnalyzers([new Parser(new \PhpParser\Parser(new Lexer())), new NameResolver(), $objectGraph = new ObjectGraph()]);
$project->analyze();
$helper = new Helper($objectGraph);
$className = 'Mfn\\PHP\\Analyzer\\Analyzers\\Analyzer';
$class = $objectGraph->getClassByFqn($className);
if (NULL === $class) {
    throw new \RuntimeException("Unable to find class {$className}");
}
unset($className);
$descendants = $helper->findExtends($class, true);
sort($descendants);
/** @var string[] $index */
$index = [];
$index[] = '# Built-in / available Analyzers';
$index[] = '';
foreach ($descendants as $class) {
    $doccomment = $class->getClass()->getDocComment();
    if (NULL === $doccomment) {
 /**
  * Recursively searches "down" the object graph to find classes, or classes
  * extended interfaces implementing this interface, which should implement
  * this method.
  *
  * Abstract classes are, like interfaces, "skipped" and their children are
  * scanned.
  *
  * @param ParsedMethod $interfaceMethod
  * @param ParsedInterface[]|ParsedClass[] $implementors
  * @return ParsedClass[]
  */
 private function findSubtypeUntilMethodMatchesRecursive(ParsedMethod $interfaceMethod, $implementors)
 {
     $classesMissingMethod = [];
     foreach ($implementors as $object) {
         # interfaces only delegate down to their implementors
         if ($object instanceof ParsedInterface) {
             $classesMissingMethod = array_merge($classesMissingMethod, $this->findSubtypeUntilMethodMatchesRecursive($interfaceMethod, $this->helper->findImplements($object)));
             continue;
         } else {
             if ($object instanceof ParsedClass) {
                 $methodName = $interfaceMethod->getNormalizedName();
                 # check if any parent implements the interface method
                 if (Helper::classImplements($object->getParent(), $interfaceMethod->getInterface()) || self::classHasParentMethod($object->getParent(), $methodName)) {
                     continue;
                 }
                 $foundMethod = false;
                 foreach ($object->getMethods() as $method) {
                     if ($methodName === $method->getNormalizedName()) {
                         $foundMethod = true;
                         break;
                     }
                 }
                 # abstract classes must not implement it ...
                 if ($object->getClass()->isAbstract()) {
                     if (!$foundMethod) {
                         # ... but we search further down the inheritance tree
                         $classesMissingMethod = array_merge($classesMissingMethod, $this->findSubtypeUntilMethodMatchesRecursive($interfaceMethod, $this->helper->findExtends($object)));
                     }
                     continue;
                 }
                 if (!$foundMethod) {
                     $classesMissingMethod[] = $object;
                 }
             }
         }
     }
     return $classesMissingMethod;
 }