public function process(XHPASTNode $root)
 {
     $parser = new PhutilDocblockParser();
     $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
     foreach ($classes as $class) {
         $is_final = false;
         $is_abstract = false;
         $is_concrete_extensible = false;
         $attributes = $class->getChildOfType(0, 'n_CLASS_ATTRIBUTES');
         foreach ($attributes->getChildren() as $child) {
             if ($child->getConcreteString() == 'final') {
                 $is_final = true;
             }
             if ($child->getConcreteString() == 'abstract') {
                 $is_abstract = true;
             }
         }
         $docblock = $class->getDocblockToken();
         if ($docblock) {
             list($text, $specials) = $parser->parse($docblock->getValue());
             $is_concrete_extensible = idx($specials, 'concrete-extensible');
         }
         if (!$is_final && !$is_abstract && !$is_concrete_extensible) {
             $this->raiseLintAtNode($class->getChildOfType(1, 'n_CLASS_NAME'), pht('This class is neither `%s` nor `%s`, and does not have ' . 'a docblock marking it `%s`.', 'final', 'abstract', '@concrete-extensible'));
         }
     }
 }
Beispiel #2
0
 public function setDocblockRaw($docblock_raw)
 {
     $this->docblockRaw = $docblock_raw;
     $parser = new PhutilDocblockParser();
     list($text, $meta) = $parser->parse($docblock_raw);
     $this->docblockText = $text;
     $this->docblockMeta = $meta;
     return $this;
 }
 private function parseDocblock($doc_file)
 {
     $contents = Filesystem::readFile($doc_file);
     $file = basename($doc_file);
     $parser = new PhutilDocblockParser();
     list($docblock, $specials) = $parser->parse($contents);
     switch ($file) {
         case 'embedded-specials.docblock':
             $this->assertEqual(array(), $specials);
             $this->assertEqual("So long as a @special does not appear at the beginning of a line,\n" . "it is parsed as normal text.", $docblock);
             break;
         case 'indented-block.docblock':
             $this->assertEqual(array(), $specials);
             $this->assertEqual('Cozy lummox gives smart squid who asks for job pen.', $docblock);
             break;
         case 'indented-text.docblock':
             $this->assertEqual(array(), $specials);
             $this->assertEqual('Cozy lummox gives smart squid who asks for job pen.', $docblock);
             break;
         case 'multiline-special.docblock':
             $this->assertEqual(array('special' => 'x y z'), $specials);
             $this->assertEqual('', $docblock);
             break;
         case 'multi-specials.docblock':
             $this->assertEqual(array('special' => array('north', 'south'), 'stable' => true), $specials);
             $this->assertEqual('', $docblock);
             break;
         case 'specials.docblock':
             $this->assertEqual(array('type' => 'type', 'task' => 'task', 'special' => array('dot', 'dot', 'dash')), $specials);
             $this->assertEqual('', $docblock);
             break;
         case 'linebreak-breaks-specials.docblock':
             $this->assertEqual(array('title' => 'title'), $specials);
             $this->assertEqual('This is normal text, not part of the @title.', $docblock);
             break;
         case 'specials-with-hyphen.docblock':
             $this->assertEqual(array('repeat-hyphen' => array('a', 'b'), 'multiline-hyphen' => 'mmm nnn', 'normal-hyphen' => 'x'), $specials);
             break;
         case 'indented-specials.docblock':
             $this->assertEqual(array('title' => 'sendmail', 'special' => 'only a little bit indented'), $specials);
             break;
         case 'flag-specials.docblock':
             $this->assertEqual("stuff above\n\nstuff in the middle\n\nstuff below", $docblock);
             $this->assertEqual(array('flag' => true, 'stuff' => true, 'zebra' => true, 'apple' => true), $specials);
             break;
         case 'mixed-types.docblock':
             $this->assertEqual(array('special' => array('squirrels', true)), $specials);
             break;
         default:
             throw new Exception(pht("No test case to handle file '%s'!", $file));
     }
 }
 private function parseDocblock($doc_file)
 {
     $contents = Filesystem::readFile($doc_file);
     $file = basename($doc_file);
     $parser = new PhutilDocblockParser();
     list($docblock, $specials) = $parser->parse($contents);
     switch ($file) {
         case 'embedded-specials.docblock':
             $this->assertEqual(array(), $specials);
             $this->assertEqual("So long as a @special does not appear at the beginning of a line,\n" . "it is parsed as normal text.", $docblock);
             break;
         case 'indented-block.docblock':
             $this->assertEqual(array(), $specials);
             $this->assertEqual("Cozy lummox gives smart squid who asks for job pen.", $docblock);
             break;
         case 'indented-text.docblock':
             $this->assertEqual(array(), $specials);
             $this->assertEqual("Cozy lummox gives smart squid who asks for job pen.", $docblock);
             break;
         case 'multiline-special.docblock':
             $this->assertEqual(array('special' => "x y z"), $specials);
             $this->assertEqual("", $docblock);
             break;
         case 'multi-specials.docblock':
             $this->assertEqual(array('special' => "north\nsouth"), $specials);
             $this->assertEqual("", $docblock);
             break;
         case 'specials.docblock':
             $this->assertEqual(array('type' => 'type', 'task' => 'task'), $specials);
             $this->assertEqual("", $docblock);
             break;
         case 'linebreak-breaks-specials.docblock':
             $this->assertEqual(array('title' => 'title'), $specials);
             $this->assertEqual("This is normal text, not part of the @title.", $docblock);
             break;
         case 'specials-with-hyphen.docblock':
             $this->assertEqual(array('repeat-hyphen' => "a\nb", 'multiline-hyphen' => "mmm nnn", 'normal-hyphen' => "x"), $specials);
             break;
         case 'indented-specials.docblock':
             $this->assertEqual(array('title' => 'sendmail'), $specials);
             break;
         case 'flag-specials.docblock':
             $this->assertEqual("stuff above\n\nstuff in the middle\n\nstuff below", $docblock);
             $this->assertEqual(array('flag' => true, 'stuff' => true, 'zebra' => true, 'apple' => true), $specials);
             break;
         default:
             throw new Exception("No test case to handle file '{$file}'!");
     }
 }
Beispiel #5
0
$files = $finder->find();
echo "Processing " . count($files) . " files";
$file_map = array();
foreach ($files as $path => $raw_hash) {
    echo ".";
    $path = '/' . Filesystem::readablePath($path, $root);
    $data = Filesystem::readFile($root . $path);
    $data = $xformer->transformResource($path, $data);
    $hash = md5($data);
    $hash = md5($hash . $path . $resource_hash);
    $file_map[$path] = array('hash' => $hash, 'disk' => $path);
}
echo "\n";
$resource_graph = array();
$hash_map = array();
$parser = new PhutilDocblockParser();
foreach ($file_map as $path => $info) {
    $type = CelerityResourceTransformer::getResourceType($path);
    $data = Filesystem::readFile($root . $info['disk']);
    $matches = array();
    $ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches);
    if (!$ok) {
        throw new Exception("File {$path} does not have a header doc comment. Encode dependency " . "data in a header docblock.");
    }
    list($description, $metadata) = $parser->parse($matches[0]);
    $provides = preg_split('/\\s+/', trim(idx($metadata, 'provides')));
    $requires = preg_split('/\\s+/', trim(idx($metadata, 'requires')));
    $provides = array_filter($provides);
    $requires = array_filter($requires);
    if (!$provides) {
        // Tests and documentation-only JS is permitted to @provide no targets.
 private function lintRaggedClasstreeEdges($root)
 {
     $parser = new PhutilDocblockParser();
     $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
     foreach ($classes as $class) {
         $is_final = false;
         $is_abstract = false;
         $is_concrete_extensible = false;
         $attributes = $class->getChildOfType(0, 'n_CLASS_ATTRIBUTES');
         foreach ($attributes->getChildren() as $child) {
             if ($child->getConcreteString() == 'final') {
                 $is_final = true;
             }
             if ($child->getConcreteString() == 'abstract') {
                 $is_abstract = true;
             }
         }
         $docblock = $class->getDocblockToken();
         if ($docblock) {
             list($text, $specials) = $parser->parse($docblock->getValue());
             $is_concrete_extensible = idx($specials, 'concrete-extensible');
         }
         if (!$is_final && !$is_abstract && !$is_concrete_extensible) {
             $this->raiseLintAtNode($class->getChildOfType(1, 'n_CLASS_NAME'), self::LINT_RAGGED_CLASSTREE_EDGE, "This class is neither 'final' nor 'abstract', and does not have " . "a docblock marking it '@concrete-extensible'.");
         }
     }
 }
Beispiel #7
0
phutil_require_module('phutil', 'parser/xhpast/api/tree');
phutil_require_module('arcanist', 'lint/linter/phutilmodule');
phutil_require_module('arcanist', 'lint/message');
phutil_require_module('arcanist', 'parser/phutilmodule');
$data = array();
$futures = array();
foreach (Filesystem::listDirectory($dir, $hidden_files = false) as $file) {
    if (!preg_match('/.php$/', $file)) {
        continue;
    }
    $data[$file] = Filesystem::readFile($dir . '/' . $file);
    $futures[$file] = xhpast_get_parser_future($data[$file]);
}
$requirements = new PhutilModuleRequirements();
$requirements->addBuiltins($builtin);
$doc_parser = new PhutilDocblockParser();
$has_init = false;
$has_files = false;
foreach (Futures($futures) as $file => $future) {
    try {
        $tree = XHPASTTree::newFromDataAndResolvedExecFuture($data[$file], $future->resolve());
    } catch (XHPASTSyntaxErrorException $ex) {
        echo "Syntax Error! In '{$file}': " . $ex->getMessage() . "\n";
        exit(1);
    }
    $root = $tree->getRootNode();
    $requirements->setCurrentFile($file);
    if ($file == '__init__.php') {
        $has_init = true;
        $calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
        foreach ($calls as $call) {
 public function parseFile($file, $data)
 {
     $ast = $this->trees[$file];
     $this->expectNode($ast, 'Program');
     $atoms = $this->parseAST($ast);
     $file_atom = new DivinerFileAtom();
     foreach ($atoms as $atom) {
         $file_atom->addChild($atom);
     }
     $file_atom->setName($file);
     $file_atom->setFile($file);
     $dparser = new PhutilDocblockParser();
     $blocks = $dparser->extractDocblocks($data);
     // Reject the first docblock as a header block.
     array_shift($blocks);
     $map = array();
     foreach ($blocks as $data) {
         list($block, $line) = $data;
         $map[$line] = $block;
     }
     $atoms = $file_atom->getAllChildren();
     $atoms = mpull($atoms, null, 'getLine');
     ksort($atoms);
     end($atoms);
     $last = key($atoms);
     $block_map = array();
     $pointer = null;
     for ($ii = 1; $ii <= $last; $ii++) {
         if (isset($map[$ii])) {
             $pointer = $ii;
         }
         $block_map[$ii] = $pointer;
     }
     foreach ($atoms as $atom) {
         $block_id = $block_map[$atom->getLine()];
         if (isset($map[$block_id])) {
             $atom->setRawDocblock($map[$block_id]);
             unset($map[$block_id]);
         }
         if ($atom instanceof DivinerMethodAtom || $atom instanceof DivinerFunctionAtom) {
             $metadata = $atom->getDocblockMetadata();
             $return = idx($metadata, 'return');
             if ($return) {
                 $split = preg_split('/\\s+/', trim($return), $limit = 2);
                 if (!empty($split[0])) {
                     $type = $split[0];
                 } else {
                     $type = 'wild';
                 }
                 $docs = null;
                 if (!empty($split[1])) {
                     $docs = $split[1];
                 }
                 $dict = array('doctype' => $type, 'docs' => $docs);
                 $atom->setReturnTypeAttributes($dict);
             }
             $docs = idx($metadata, 'param', '');
             if ($docs) {
                 $docs = explode("\n", $docs);
                 foreach ($atom->getParameters() as $param => $dict) {
                     $doc = array_shift($docs);
                     if ($doc) {
                         $dict += $this->parseParamDoc($doc);
                     }
                     $atom->addParameter($param, $dict);
                 }
                 // Add extra parameters retrieved by arguments variable.
                 foreach ($docs as $doc) {
                     if ($doc) {
                         $atom->addParameter('', $this->parseParamDoc($doc));
                     }
                 }
             }
         }
     }
     foreach ($atoms as $atom) {
         $atom->setLanguage('js');
         $atom->setFile($file);
     }
     $file_atom->setLanguage('js');
     return array($file_atom);
 }
 /**
  * Parse the `@provides` and `@requires` symbols out of a text resource, like
  * JS or CSS.
  *
  * @param string Resource name.
  * @param string Resource data.
  * @return pair<string|null, list<string>|null> The `@provides` symbol and the
  *    list of `@requires` symbols. If the resource is not part of the
  *    dependency graph, both are null.
  */
 private function getProvidesAndRequires($name, $data)
 {
     $parser = new PhutilDocblockParser();
     $matches = array();
     $ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches);
     if (!$ok) {
         throw new Exception(pht('Resource "%s" does not have a header doc comment. Encode ' . 'dependency data in a header docblock.', $name));
     }
     list($description, $metadata) = $parser->parse($matches[0]);
     $provides = preg_split('/\\s+/', trim(idx($metadata, 'provides')));
     $requires = preg_split('/\\s+/', trim(idx($metadata, 'requires')));
     $provides = array_filter($provides);
     $requires = array_filter($requires);
     if (!$provides) {
         // Tests and documentation-only JS is permitted to @provide no targets.
         return array(null, null);
     }
     if (count($provides) > 1) {
         throw new Exception(pht('Resource "%s" must @provide at most one Celerity target.', $name));
     }
     return array(head($provides), $requires);
 }