Esempio n. 1
0
/**
 * @group xhpast
 */
function xhpast_get_parser_future($data)
{
    if (!xhpast_is_available()) {
        throw new Exception(xhpast_get_build_instructions());
    }
    $future = new ExecFuture('%s', xhpast_get_binary_path());
    $future->write($data);
    return $future;
}
Esempio n. 2
0
/**
 * @group xhpast
 */
function xhpast_get_parser_future($data)
{
    if (!xhpast_is_available()) {
        try {
            // Try to build XHPAST automatically. If we can't then just ask the user
            // to build it themselves.
            xhpast_build();
        } catch (CommandException $e) {
            throw new Exception(xhpast_get_build_instructions());
        }
    }
    $future = new ExecFuture('%s', xhpast_get_binary_path());
    $future->write($data);
    return $future;
}
 public function willLintPaths(array $paths)
 {
     if (!xhpast_is_available()) {
         throw new Exception(xhpast_get_build_instructions());
     }
     // NOTE: For now, we completely ignore paths and just lint every library in
     // its entirety. This is simpler and relatively fast because we don't do any
     // detailed checks and all the data we need for this comes out of module
     // caches.
     $bootloader = PhutilBootloader::getInstance();
     $libs = $bootloader->getAllLibraries();
     // Load the up-to-date map for each library, without loading the library
     // itself. This means lint results will accurately reflect the state of
     // the working copy.
     $arc_root = dirname(phutil_get_library_root('arcanist'));
     $bin = "{$arc_root}/scripts/phutil_rebuild_map.php";
     $symbols = array();
     foreach ($libs as $lib) {
         // Do these one at a time since they individually fanout to saturate
         // available system resources.
         $future = new ExecFuture('%s --show --quiet --ugly -- %s', $bin, phutil_get_library_root($lib));
         $symbols[$lib] = $future->resolveJSON();
     }
     $all_symbols = array();
     foreach ($symbols as $library => $map) {
         // Check for files which declare more than one class/interface in the same
         // file, or mix function definitions with class/interface definitions. We
         // must isolate autoloadable symbols to one per file so the autoloader
         // can't end up in an unresolvable cycle.
         foreach ($map as $file => $spec) {
             $have = idx($spec, 'have', array());
             $have_classes = idx($have, 'class', array()) + idx($have, 'interface', array());
             $have_functions = idx($have, 'function');
             if ($have_functions && $have_classes) {
                 $function_list = implode(', ', array_keys($have_functions));
                 $class_list = implode(', ', array_keys($have_classes));
                 $this->raiseLintInLibrary($library, $file, end($have_functions), self::LINT_ONE_CLASS_PER_FILE, "File '{$file}' mixes function ({$function_list}) and " . "class/interface ({$class_list}) definitions in the same file. " . "A file which declares a class or an interface MUST " . "declare nothing else.");
             } else {
                 if (count($have_classes) > 1) {
                     $class_list = implode(', ', array_keys($have_classes));
                     $this->raiseLintInLibrary($library, $file, end($have_classes), self::LINT_ONE_CLASS_PER_FILE, "File '{$file}' declares more than one class or interface " . "({$class_list}). A file which declares a class or interface MUST " . "declare nothing else.");
                 }
             }
         }
         // Check for duplicate symbols: two files providing the same class or
         // function.
         foreach ($map as $file => $spec) {
             $have = idx($spec, 'have', array());
             foreach (array('class', 'function', 'interface') as $type) {
                 $libtype = $type == 'interface' ? 'class' : $type;
                 foreach (idx($have, $type, array()) as $symbol => $offset) {
                     if (empty($all_symbols[$libtype][$symbol])) {
                         $all_symbols[$libtype][$symbol] = array('library' => $library, 'file' => $file, 'offset' => $offset);
                         continue;
                     }
                     $osrc = $all_symbols[$libtype][$symbol]['file'];
                     $olib = $all_symbols[$libtype][$symbol]['library'];
                     $this->raiseLintInLibrary($library, $file, $offset, self::LINT_DUPLICATE_SYMBOL, "Definition of {$type} '{$symbol}' in '{$file}' in library " . "'{$library}' duplicates prior definition in '{$osrc}' in " . "library '{$olib}'.");
                 }
             }
         }
     }
     foreach ($symbols as $library => $map) {
         // Check for unknown symbols: uses of classes, functions or interfaces
         // which are not defined anywhere. We reference the list of all symbols
         // we built up earlier.
         foreach ($map as $file => $spec) {
             $need = idx($spec, 'need', array());
             foreach (array('class', 'function', 'interface') as $type) {
                 $libtype = $type == 'interface' ? 'class' : $type;
                 foreach (idx($need, $type, array()) as $symbol => $offset) {
                     if (!empty($all_symbols[$libtype][$symbol])) {
                         // Symbol is defined somewhere.
                         continue;
                     }
                     $this->raiseLintInLibrary($library, $file, $offset, self::LINT_UNKNOWN_SYMBOL, "Use of unknown {$type} '{$symbol}'. This symbol is not defined " . "in any loaded phutil library.");
                 }
             }
         }
     }
 }
 public function willLintPaths(array $paths)
 {
     if (!xhpast_is_available()) {
         throw new Exception(xhpast_get_build_instructions());
     }
     // NOTE: For now, we completely ignore paths and just lint every library in
     // its entirety. This is simpler and relatively fast because we don't do any
     // detailed checks and all the data we need for this comes out of module
     // caches.
     $bootloader = PhutilBootloader::getInstance();
     $libs = $bootloader->getAllLibraries();
     // Load the up-to-date map for each library, without loading the library
     // itself. This means lint results will accurately reflect the state of
     // the working copy.
     $symbols = array();
     foreach ($libs as $lib) {
         $root = phutil_get_library_root($lib);
         try {
             $symbols[$lib] = id(new PhutilLibraryMapBuilder($root))->buildFileSymbolMap();
         } catch (XHPASTSyntaxErrorException $ex) {
             // If the library contains a syntax error then there isn't much that we
             // can do.
             continue;
         }
     }
     $all_symbols = array();
     foreach ($symbols as $library => $map) {
         // Check for files which declare more than one class/interface in the same
         // file, or mix function definitions with class/interface definitions. We
         // must isolate autoloadable symbols to one per file so the autoloader
         // can't end up in an unresolvable cycle.
         foreach ($map as $file => $spec) {
             $have = idx($spec, 'have', array());
             $have_classes = idx($have, 'class', array()) + idx($have, 'interface', array());
             $have_functions = idx($have, 'function');
             if ($have_functions && $have_classes) {
                 $function_list = implode(', ', array_keys($have_functions));
                 $class_list = implode(', ', array_keys($have_classes));
                 $this->raiseLintInLibrary($library, $file, end($have_functions), self::LINT_ONE_CLASS_PER_FILE, "File '{$file}' mixes function ({$function_list}) and " . "class/interface ({$class_list}) definitions in the same file. " . "A file which declares a class or an interface MUST " . "declare nothing else.");
             } else {
                 if (count($have_classes) > 1) {
                     $class_list = implode(', ', array_keys($have_classes));
                     $this->raiseLintInLibrary($library, $file, end($have_classes), self::LINT_ONE_CLASS_PER_FILE, "File '{$file}' declares more than one class or interface " . "({$class_list}). A file which declares a class or interface MUST " . "declare nothing else.");
                 }
             }
         }
         // Check for duplicate symbols: two files providing the same class or
         // function.
         foreach ($map as $file => $spec) {
             $have = idx($spec, 'have', array());
             foreach (array('class', 'function', 'interface') as $type) {
                 $libtype = $type == 'interface' ? 'class' : $type;
                 foreach (idx($have, $type, array()) as $symbol => $offset) {
                     if (empty($all_symbols[$libtype][$symbol])) {
                         $all_symbols[$libtype][$symbol] = array('library' => $library, 'file' => $file, 'offset' => $offset);
                         continue;
                     }
                     $osrc = $all_symbols[$libtype][$symbol]['file'];
                     $olib = $all_symbols[$libtype][$symbol]['library'];
                     $this->raiseLintInLibrary($library, $file, $offset, self::LINT_DUPLICATE_SYMBOL, "Definition of {$type} '{$symbol}' in '{$file}' in library " . "'{$library}' duplicates prior definition in '{$osrc}' in " . "library '{$olib}'.");
                 }
             }
         }
     }
     $types = array('class', 'function', 'interface', 'class/interface');
     foreach ($symbols as $library => $map) {
         // Check for unknown symbols: uses of classes, functions or interfaces
         // which are not defined anywhere. We reference the list of all symbols
         // we built up earlier.
         foreach ($map as $file => $spec) {
             $need = idx($spec, 'need', array());
             foreach ($types as $type) {
                 $libtype = $type;
                 if ($type == 'interface' || $type == 'class/interface') {
                     $libtype = 'class';
                 }
                 foreach (idx($need, $type, array()) as $symbol => $offset) {
                     if (!empty($all_symbols[$libtype][$symbol])) {
                         // Symbol is defined somewhere.
                         continue;
                     }
                     $libphutil_root = dirname(phutil_get_library_root('phutil'));
                     $this->raiseLintInLibrary($library, $file, $offset, self::LINT_UNKNOWN_SYMBOL, "Use of unknown {$type} '{$symbol}'. Common causes are:\n\n" . "  - Your libphutil/ is out of date.\n" . "    This is the most common cause.\n" . "    Update this copy of libphutil: {$libphutil_root}\n" . "\n" . "  - Some other library is out of date.\n" . "    Update the library this symbol appears in.\n" . "\n" . "  - This symbol is misspelled.\n" . "    Spell the symbol name correctly.\n" . "    Symbol name spelling is case-sensitive.\n" . "\n" . "  - This symbol was added recently.\n" . "    Run `arc liberate` on the library it was added to.\n" . "\n" . "  - This symbol is external. Use `@phutil-external-symbol`.\n" . "    Use `grep` to find usage examples of this directive.\n" . "\n" . "*** ALTHOUGH USUALLY EASY TO FIX, THIS IS A SERIOUS ERROR.\n" . "*** THIS ERROR IS YOUR FAULT. YOU MUST RESOLVE IT.");
                 }
             }
         }
     }
 }
 public function willLintPaths(array $paths)
 {
     if ($paths) {
         if (!xhpast_is_available()) {
             throw new Exception(xhpast_get_build_instructions());
         }
     }
     $modules = array();
     $moduleinfo = array();
     $project_root = $this->getEngine()->getWorkingCopy()->getProjectRoot();
     foreach ($paths as $path) {
         $absolute_path = $project_root . '/' . $path;
         $library_root = phutil_get_library_root_for_path($absolute_path);
         if (!$library_root) {
             continue;
         }
         if ($this->isPhutilLibraryMetadata($path)) {
             continue;
         }
         $library_name = phutil_get_library_name_for_root($library_root);
         if (!is_dir($path)) {
             $path = dirname($path);
         }
         $path = Filesystem::resolvePath($path, $project_root);
         if ($path == $library_root) {
             continue;
         }
         $module_name = Filesystem::readablePath($path, $library_root);
         $module_key = $library_name . ':' . $module_name;
         if (empty($modules[$module_key])) {
             $modules[$module_key] = $module_key;
             $this->setModuleInfo($module_key, array('library' => $library_name, 'root' => $library_root, 'module' => $module_name));
         }
     }
     if (!$modules) {
         return;
     }
     $modules = array_keys($modules);
     $arc_root = phutil_get_library_root('arcanist');
     $bin = dirname($arc_root) . '/scripts/phutil_analyzer.php';
     $futures = array();
     foreach ($modules as $mkey => $key) {
         $disk_path = $this->getModulePathOnDisk($key);
         if (Filesystem::pathExists($disk_path)) {
             $futures[$key] = new ExecFuture('%s %s', $bin, $disk_path);
         } else {
             // This can occur in git when you add a module in HEAD and then remove
             // it in unstaged changes in the working copy. Just ignore it.
             unset($modules[$mkey]);
         }
     }
     $requirements = array();
     foreach (Futures($futures)->limit(16) as $key => $future) {
         $requirements[$key] = $future->resolveJSON();
     }
     $dependencies = array();
     $futures = array();
     foreach ($requirements as $key => $requirement) {
         foreach ($requirement['messages'] as $message) {
             list($where, $text, $code, $description) = $message;
             if ($where) {
                 $where = array($where);
             }
             $this->raiseLintInModule($key, $code, $description, $where, $text);
         }
         foreach ($requirement['requires']['module'] as $req_module => $where) {
             if (isset($requirements[$req_module])) {
                 $dependencies[$req_module] = $requirements[$req_module];
             } else {
                 list($library_name, $module_name) = explode(':', $req_module);
                 $library_root = phutil_get_library_root($library_name);
                 $this->setModuleInfo($req_module, array('library' => $library_name, 'root' => $library_root, 'module' => $module_name));
                 $disk_path = $this->getModulePathOnDisk($req_module);
                 if (Filesystem::pathExists($disk_path)) {
                     $futures[$req_module] = new ExecFuture('%s %s', $bin, $disk_path);
                 } else {
                     $dependencies[$req_module] = array();
                 }
             }
         }
     }
     foreach (Futures($futures)->limit(16) as $key => $future) {
         $dependencies[$key] = $future->resolveJSON();
     }
     foreach ($requirements as $key => $spec) {
         $deps = array_intersect_key($dependencies, $spec['requires']['module']);
         $this->lintModule($key, $spec, $deps);
     }
 }