diff --git a/scripts/phutil_analyzer.php b/scripts/phutil_analyzer.php deleted file mode 100755 index 8b800680..00000000 --- a/scripts/phutil_analyzer.php +++ /dev/null @@ -1,409 +0,0 @@ -#!/usr/bin/env php - array_fill_keys($builtin_classes, true) + array( - 'PhutilBootloader' => true, - ), - 'function' => array_filter( - array( - 'empty' => true, - 'isset' => true, - 'echo' => true, - 'print' => true, - 'exit' => true, - 'die' => true, - - // These are provided by libphutil but not visible in the map. - - 'phutil_is_windows' => true, - 'phutil_load_library' => true, - 'phutil_is_hiphop_runtime' => true, - - // HPHP/i defines these functions as 'internal', but they are NOT - // builtins and do not exist in vanilla PHP. Make sure we don't mark them - // as builtin since we need to add dependencies for them. - 'idx' => false, - 'id' => false, - ) + array_fill_keys($builtin_functions, true)), - 'interface' => array_fill_keys($builtin_interfaces, true), -); - -require_once dirname(__FILE__).'/__init_script__.php'; - -if ($argc != 2) { - $self = basename($argv[0]); - echo "usage: {$self} \n"; - exit(1); -} - -phutil_require_module('phutil', 'filesystem'); -$dir = Filesystem::resolvePath($argv[1]); - - -$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) { - $name = $call->getChildByIndex(0); - $call_name = $name->getConcreteString(); - if ($call_name == 'phutil_require_source') { - $params = $call->getChildByIndex(1)->getChildren(); - if (count($params) !== 1) { - $requirements->addLint( - $call, - $call->getConcreteString(), - ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE, - "Call to phutil_require_source() must have exactly one argument."); - continue; - } - $param = reset($params); - $value = $param->getStringLiteralValue(); - if ($value === null) { - $requirements->addLint( - $param, - $param->getConcreteString(), - ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE, - "phutil_require_source() parameter must be a string literal."); - continue; - } - $requirements->addSourceDependency($name, $value); - } else if ($call_name == 'phutil_require_module') { - analyze_phutil_require_module($call, $requirements, true); - } - } - } else { - $has_files = true; - - $requirements->addSourceDeclaration(basename($file)); - - // Find symbols declared as "@phutil-external-symbol function example", - // and ignore these in building dependency lists. - - $externals = array(); - foreach ($root->getTokens() as $token) { - if ($token->getTypeName() == 'T_DOC_COMMENT') { - list($block, $special) = $doc_parser->parse($token->getValue()); - - $ext_list = idx($special, 'phutil-external-symbol'); - $ext_list = explode("\n", $ext_list); - $ext_list = array_filter($ext_list); - - foreach ($ext_list as $ext_ref) { - $matches = null; - if (preg_match('/^\s*(\S+)\s+(\S+)/', $ext_ref, $matches)) { - $externals[$matches[1]][$matches[2]] = true; - } - } - } - } - - - // Function uses: - // - Explicit call - // TODO?: String literal in ReflectionFunction(). - - $calls = $root->selectDescendantsOfType('n_FUNCTION_CALL'); - foreach ($calls as $call) { - $name = $call->getChildByIndex(0); - if ($name->getTypeName() == 'n_VARIABLE' || - $name->getTypeName() == 'n_VARIABLE_VARIABLE') { - $requirements->addLint( - $name, - $name->getConcreteString(), - ArcanistPhutilModuleLinter::LINT_ANALYZER_DYNAMIC, - "Use of variable function calls prevents dependencies from being ". - "checked statically. This module may have undetectable errors."); - continue; - } - if ($name->getTypeName() == 'n_CLASS_STATIC_ACCESS') { - // We'll pick this up later. - continue; - } - - $call_name = $name->getConcreteString(); - if ($call_name == 'phutil_require_module') { - analyze_phutil_require_module($call, $requirements, false); - } else if ($call_name == 'call_user_func' || - $call_name == 'call_user_func_array') { - $params = $call->getChildByIndex(1)->getChildren(); - if (count($params) == 0) { - $requirements->addLint( - $call, - $call->getConcreteString(), - ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE, - "Call to {$call_name}() must have at least one argument."); - } - $symbol = array_shift($params); - $symbol_value = $symbol->getStringLiteralValue(); - if ($symbol_value) { - $requirements->addFunctionDependency( - $symbol, - $symbol_value); - } else { - $requirements->addLint( - $symbol, - $symbol->getConcreteString(), - ArcanistPhutilModuleLinter::LINT_ANALYZER_DYNAMIC, - "Use of variable arguments to {$call_name} prevents dependencies ". - "from being checked statically. This module may have undetectable ". - "errors."); - } - } else if (empty($externals['function'][$name->getConcreteString()])) { - $requirements->addFunctionDependency( - $name, - $name->getConcreteString()); - } - } - - $functions = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION'); - foreach ($functions as $function) { - $name = $function->getChildByIndex(2); - if ($name->getTypeName() == 'n_EMPTY') { - // This is an anonymous function; don't record it into the symbol - // index. - } else { - $requirements->addFunctionDeclaration( - $name, - $name->getConcreteString()); - } - } - - - // Class uses: - // - new - // - extends (in class declaration) - // - Static method call - // - Static property access - // - Constant use - // TODO?: String literal in ReflectionClass(). - // TODO?: String literal in array literal in call_user_func / - // call_user_func_array(). - - // TODO: Raise a soft warning for use of an unknown class in: - // - Typehints - // - instanceof - // - catch - - $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); - foreach ($classes as $class) { - $class_name = $class->getChildByIndex(1); - $requirements->addClassDeclaration( - $class_name, - $class_name->getConcreteString()); - $extends = $class->getChildByIndex(2); - foreach ($extends->selectDescendantsOfType('n_CLASS_NAME') as $parent) { - if (empty($externals['class'][$parent->getConcreteString()])) { - $requirements->addClassDependency( - $class_name->getConcreteString(), - $parent, - $parent->getConcreteString()); - } - } - $implements = $class->getChildByIndex(3); - $interfaces = $implements->selectDescendantsOfType('n_CLASS_NAME'); - foreach ($interfaces as $interface) { - if (empty($externals['interface'][$interface->getConcreteString()])) { - $requirements->addInterfaceDependency( - $class_name->getConcreteString(), - $interface, - $interface->getConcreteString()); - } - } - } - - if (count($classes) > 1) { - foreach ($classes as $class) { - $class_name = $class->getChildByIndex(1); - $class_string = $class_name->getConcreteString(); - $requirements->addLint( - $class_name, - $class_string, - ArcanistPhutilModuleLinter::LINT_ANALYZER_MULTIPLE_CLASSES, - "This file declares more than one class. Declare only one class per ". - "file."); - break; - } - } - - $uses_of_new = $root->selectDescendantsOfType('n_NEW'); - foreach ($uses_of_new as $new_operator) { - $name = $new_operator->getChildByIndex(0); - if ($name->getTypeName() == 'n_VARIABLE' || - $name->getTypeName() == 'n_VARIABLE_VARIABLE') { - $requirements->addLint( - $name, - $name->getConcreteString(), - ArcanistPhutilModuleLinter::LINT_ANALYZER_DYNAMIC, - "Use of variable class instantiation prevents dependencies from ". - "being checked statically. This module may have undetectable ". - "errors."); - continue; - } - if (empty($externals['class'][$name->getConcreteString()])) { - $requirements->addClassDependency( - null, - $name, - $name->getConcreteString()); - } - } - - $static_uses = $root->selectDescendantsOfType('n_CLASS_STATIC_ACCESS'); - foreach ($static_uses as $static_use) { - $name = $static_use->getChildByIndex(0); - if ($name->getTypeName() != 'n_CLASS_NAME') { - echo "WARNING UNLINTABLE\n"; - continue; - } - $name_concrete = $name->getConcreteString(); - $magic_names = array( - 'static' => true, - 'parent' => true, - 'self' => true, - ); - if (isset($magic_names[$name_concrete])) { - continue; - } - if (empty($externals['class'][$name_concrete])) { - $requirements->addClassDependency( - null, - $name, - $name_concrete); - } - } - - // Interface uses: - // - implements - // - extends (in interface declaration) - - $interfaces = $root->selectDescendantsOfType('n_INTERFACE_DECLARATION'); - foreach ($interfaces as $interface) { - $interface_name = $interface->getChildByIndex(1); - $requirements->addInterfaceDeclaration( - $interface_name, - $interface_name->getConcreteString()); - $extends = $interface->getChildByIndex(2); - foreach ($extends->selectDescendantsOfType('n_CLASS_NAME') as $parent) { - if (empty($externals['interface'][$parent->getConcreteString()])) { - $requirements->addInterfaceDependency( - $interface_name->getConcreteString(), - $parent, - $parent->getConcreteString()); - } - } - } - - } -} - -if (!$has_init && $has_files) { - $requirements->addRawLint( - ArcanistPhutilModuleLinter::LINT_ANALYZER_NO_INIT, - "Create an __init__.php file in this module."); -} - -echo json_encode($requirements->toDictionary()); - -/** - * Parses meaning from calls to phutil_require_module() in __init__.php files. - * - * @group module - */ -function analyze_phutil_require_module( - XHPASTNode $call, - PhutilModuleRequirements $requirements, - $create_dependency) { - - $name = $call->getChildByIndex(0); - $params = $call->getChildByIndex(1)->getChildren(); - if (count($params) !== 2) { - $requirements->addLint( - $call, - $call->getConcreteString(), - ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE, - "Call to phutil_require_module() must have exactly two arguments."); - return; - } - - $module_param = array_pop($params); - $library_param = array_pop($params); - - $library_value = $library_param->getStringLiteralValue(); - if ($library_value === null) { - $requirements->addLint( - $library_param, - $library_param->getConcreteString(), - ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE, - "phutil_require_module() parameters must be string literals."); - return; - } - - $module_value = $module_param->getStringLiteralValue(); - if ($module_value === null) { - $requirements->addLint( - $module_param, - $module_param->getConcreteString(), - ArcanistPhutilModuleLinter::LINT_ANALYZER_SIGNATURE, - "phutil_require_module() parameters must be string literals."); - return; - } - - if ($create_dependency) { - $requirements->addModuleDependency( - $name, - $library_value.':'.$module_value); - } -} diff --git a/scripts/phutil_mapper.php b/scripts/phutil_mapper.php deleted file mode 100755 index b0da9660..00000000 --- a/scripts/phutil_mapper.php +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env php -\n"; - exit(1); -} - -$root = Filesystem::resolvePath($argv[1]); - -if (!@file_exists($root.'/__phutil_library_init__.php')) { - throw new Exception("Provided path is not a phutil library."); -} - -if ($liberate_mode) { - ob_start(); -} - -echo "Finding phutil modules...\n"; -$files = id(new FileFinder($root)) - ->withType('f') - ->withSuffix('php') - ->excludePath('*/.*') - ->setGenerateChecksums(true) - ->find(); - -// NOTE: Sorting by filename ensures that hash computation is stable; it is -// important we sort by name instead of by hash because sorting by hash could -// create a bad cache hit if the user swaps the contents of two files. -ksort($files); - -$modules = array(); -foreach ($files as $file => $hash) { - if (dirname($file) == $root) { - continue; - } - $modules[Filesystem::readablePath(dirname($file), $root)][] = $hash; -} - -echo "Found ".count($files)." files in ".count($modules)." modules.\n"; - -$signatures = array(); -foreach ($modules as $module => $hashes) { - $hashes = implode(' ', $hashes); - $signature = md5($hashes); - $signatures[$module] = $signature; -} - -try { - $cache = Filesystem::readFile($root.'/.phutil_module_cache'); -} catch (Exception $ex) { - $cache = null; -} - -$signature_cache = array(); -if ($cache) { - $signature_cache = json_decode($cache, true); - if (!is_array($signature_cache)) { - $signature_cache = array(); - } -} - -$specs = array(); - -$need_update = array(); -foreach ($signatures as $module => $signature) { - if (isset($signature_cache[$module]) && - $signature_cache[$module]['signature'] == $signature) { - $specs[$module] = $signature_cache[$module]; - } else { - $need_update[$module] = true; - } -} - -$futures = array(); -foreach ($need_update as $module => $ignored) { - $futures[$module] = new ExecFuture( - '%s %s', - dirname(__FILE__).'/phutil_analyzer.php', - $root.'/'.$module); -} - -if ($futures) { - echo "Found ".count($specs)." modules in cache; ". - "analyzing ".count($futures)." modified modules"; - foreach (Futures($futures)->limit(8) as $module => $future) { - echo "."; - $specs[$module] = array( - 'signature' => $signatures[$module], - 'spec' => $future->resolveJSON(), - ); - } - echo "\n"; -} else { - echo "All modules were found in cache.\n"; -} - -$class_map = array(); -$requires_class_map = array(); -$requires_interface_map = array(); -$function_map = array(); -foreach ($specs as $module => $info) { - $spec = $info['spec']; - foreach (array('class', 'interface') as $type) { - foreach ($spec['declares'][$type] as $class => $where) { - if (!empty($class_map[$class])) { - $prior = $class_map[$class]; - echo "\n"; - echo "Error: definition of {$type} '{$class}' in module '{$module}' ". - "duplicates prior definition in module '{$prior}'."; - echo "\n"; - exit(1); - } - $class_map[$class] = $module; - } - } - if (!empty($spec['chain']['class'])) { - $requires_class_map += $spec['chain']['class']; - } - if (!empty($spec['chain']['interface'])) { - $requires_interface_map += $spec['chain']['interface']; - } - foreach ($spec['declares']['function'] as $function => $where) { - if (!empty($function_map[$function])) { - $prior = $function_map[$function]; - echo "\n"; - echo "Error: definition of function '{$function}' in module '{$module}' ". - "duplicates prior definition in module '{$prior}'."; - echo "\n"; - exit(1); - } - $function_map[$function] = $module; - } -} -echo "\n"; - -ksort($class_map); -ksort($requires_class_map); -ksort($requires_interface_map); -ksort($function_map); - -$library_map = array( - 'class' => $class_map, - 'function' => $function_map, - 'requires_class' => $requires_class_map, - 'requires_interface' => $requires_interface_map, -); -$library_map = var_export($library_map, $return_string = true); -$library_map = preg_replace('/\s+$/m', '', $library_map); -$library_map = preg_replace('/array \(/', 'array(', $library_map); - -$at = '@'; -$map_file = << 'workflow/ArcanistInstallCertificateWorkflow.php', 'ArcanistJSHintLinter' => 'lint/linter/ArcanistJSHintLinter.php', 'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php', - 'ArcanistLiberateLintEngine' => 'lint/engine/ArcanistLiberateLintEngine.php', 'ArcanistLiberateWorkflow' => 'workflow/ArcanistLiberateWorkflow.php', 'ArcanistLicenseLinter' => 'lint/linter/ArcanistLicenseLinter.php', 'ArcanistLintConsoleRenderer' => 'lint/renderer/ArcanistLintConsoleRenderer.php', @@ -93,7 +92,6 @@ phutil_register_library_map(array( 'ArcanistPatchWorkflow' => 'workflow/ArcanistPatchWorkflow.php', 'ArcanistPhpcsLinter' => 'lint/linter/ArcanistPhpcsLinter.php', 'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php', - 'ArcanistPhutilModuleLinter' => 'lint/linter/ArcanistPhutilModuleLinter.php', 'ArcanistPhutilTestCase' => 'unit/engine/phutil/ArcanistPhutilTestCase.php', 'ArcanistPhutilTestCaseTestCase' => 'unit/engine/phutil/testcase/ArcanistPhutilTestCaseTestCase.php', 'ArcanistPhutilTestSkippedException' => 'unit/engine/phutil/testcase/ArcanistPhutilTestSkippedException.php', @@ -178,7 +176,6 @@ phutil_register_library_map(array( 'ArcanistInstallCertificateWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistJSHintLinter' => 'ArcanistLinter', 'ArcanistLandWorkflow' => 'ArcanistBaseWorkflow', - 'ArcanistLiberateLintEngine' => 'ArcanistLintEngine', 'ArcanistLiberateWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistLicenseLinter' => 'ArcanistLinter', 'ArcanistLintConsoleRenderer' => 'ArcanistLintRenderer', @@ -200,7 +197,6 @@ phutil_register_library_map(array( 'ArcanistPatchWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistPhpcsLinter' => 'ArcanistLinter', 'ArcanistPhutilLibraryLinter' => 'ArcanistLinter', - 'ArcanistPhutilModuleLinter' => 'ArcanistLinter', 'ArcanistPhutilTestCaseTestCase' => 'ArcanistPhutilTestCase', 'ArcanistPhutilTestSkippedException' => 'Exception', 'ArcanistPhutilTestTerminatedException' => 'Exception', diff --git a/src/lint/engine/ArcanistLiberateLintEngine.php b/src/lint/engine/ArcanistLiberateLintEngine.php deleted file mode 100644 index 3b1d9e47..00000000 --- a/src/lint/engine/ArcanistLiberateLintEngine.php +++ /dev/null @@ -1,38 +0,0 @@ -getPaths() as $path) { - $module_linter->addPath($path); - } - - return array($module_linter); - } - -} diff --git a/src/lint/linter/ArcanistPhutilModuleLinter.php b/src/lint/linter/ArcanistPhutilModuleLinter.php deleted file mode 100644 index b7f58d67..00000000 --- a/src/lint/linter/ArcanistPhutilModuleLinter.php +++ /dev/null @@ -1,536 +0,0 @@ - 'Use of Undeclared Class', - self::LINT_UNDECLARED_FUNCTION => 'Use of Undeclared Function', - self::LINT_UNDECLARED_INTERFACE => 'Use of Undeclared Interface', - self::LINT_UNDECLARED_SOURCE => 'Use of Nonexistent File', - self::LINT_UNUSED_SOURCE => 'Unused Source', - self::LINT_UNUSED_MODULE => 'Unused Module', - self::LINT_INIT_REBUILD => 'Rebuilt __init__.php File', - self::LINT_UNKNOWN_CLASS => 'Unknown Class', - self::LINT_UNKNOWN_FUNCTION => 'Unknown Function', - self::LINT_ANALYZER_SIGNATURE => 'Analyzer: Bad Call Signature', - self::LINT_ANALYZER_DYNAMIC => 'Analyzer: Dynamic Dependency', - self::LINT_ANALYZER_NO_INIT => 'Analyzer: No __init__.php File', - self::LINT_ANALYZER_MULTIPLE_CLASSES - => 'Analyzer: File Declares Multiple Classes', - ); - } - - public function getLinterName() { - return 'PHU'; - } - - public function getLintSeverityMap() { - return array( - self::LINT_ANALYZER_DYNAMIC => ArcanistLintSeverity::SEVERITY_WARNING, - ); - } - - private $moduleInfo = array(); - private $unknownClasses = array(); - private $unknownFunctions = array(); - - private function setModuleInfo($key, array $info) { - $this->moduleInfo[$key] = $info; - return $this; - } - - private function getModulePathOnDisk($key) { - $info = $this->moduleInfo[$key]; - return $info['root'].'/'.$info['module']; - } - - private function getModuleDisplayName($key) { - $info = $this->moduleInfo[$key]; - return $info['module']; - } - - private function isPhutilLibraryMetadata($path) { - $file = basename($path); - return !strncmp('__phutil_library_', $file, strlen('__phutil_library_')); - } - - 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(); - $bootloader = PhutilBootloader::getInstance(); - - 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); - - $version = $bootloader->getLibraryFormatVersion($library_name); - if ($version != 1) { - continue; - } - - 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); - } - } - - private function lintModule($key, $spec, $deps) { - $resolvable = array(); - $need_classes = array(); - $need_functions = array(); - $drop_modules = array(); - - $used = array(); - static $types = array( - 'class' => self::LINT_UNDECLARED_CLASS, - 'interface' => self::LINT_UNDECLARED_INTERFACE, - 'function' => self::LINT_UNDECLARED_FUNCTION, - ); - foreach ($types as $type => $lint_code) { - foreach ($spec['requires'][$type] as $name => $places) { - $declared = $this->checkDependency( - $type, - $name, - $deps); - if (!$declared) { - $module = $this->getModuleDisplayName($key); - $message = $this->raiseLintInModule( - $key, - $lint_code, - "Module '{$module}' uses {$type} '{$name}' but does not include ". - "any module which declares it.", - $places); - - if ($type == 'class' || $type == 'interface') { - $loader = new PhutilSymbolLoader(); - $loader->setType($type); - $loader->setName($name); - $symbols = $loader->selectSymbolsWithoutLoading(); - if ($symbols) { - $class_spec = reset($symbols); - try { - $loader->selectAndLoadSymbols(); - $loaded = true; - } catch (PhutilMissingSymbolException $ex) { - $loaded = false; - } catch (PhutilBootloaderException $ex) { - $loaded = false; - } - if ($loaded) { - $resolvable[] = $message; - $need_classes[$name] = $class_spec; - } else { - if (empty($this->unknownClasses[$name])) { - $this->unknownClasses[$name] = true; - $library = $class_spec['library']; - $this->raiseLintInModule( - $key, - self::LINT_UNKNOWN_CLASS, - "Class '{$name}' exists in the library map for library ". - "'{$library}', but could not be loaded. You may need to ". - "rebuild the library map.", - $places); - } - } - } else { - if (empty($this->unknownClasses[$name])) { - $this->unknownClasses[$name] = true; - $this->raiseLintInModule( - $key, - self::LINT_UNKNOWN_CLASS, - "Class '{$name}' could not be found in any known library. ". - "You may need to rebuild the map for the library which ". - "contains it.", - $places); - } - } - } else { - $loader = new PhutilSymbolLoader(); - $loader->setType($type); - $loader->setName($name); - $symbols = $loader->selectSymbolsWithoutLoading(); - if ($symbols) { - $func_spec = reset($symbols); - try { - $loader->selectAndLoadSymbols(); - $loaded = true; - } catch (PhutilMissingSymbolException $ex) { - $loaded = false; - } catch (PhutilBootloaderException $ex) { - $loaded = false; - } - if ($loaded) { - $resolvable[] = $message; - $need_functions[$name] = $func_spec; - } else { - if (empty($this->unknownFunctions[$name])) { - $this->unknownFunctions[$name] = true; - $library = $func_spec['library']; - $this->raiseLintInModule( - $key, - self::LINT_UNKNOWN_FUNCTION, - "Function '{$name}' exists in the library map for library ". - "'{$library}', but could not be loaded. You may need to ". - "rebuild the library map.", - $places); - } - } - } else { - if (empty($this->unknownFunctions[$name])) { - $this->unknownFunctions[$name] = true; - $this->raiseLintInModule( - $key, - self::LINT_UNKNOWN_FUNCTION, - "Function '{$name}' could not be found in any known ". - "library. You may need to rebuild the map for the library ". - "which contains it.", - $places); - } - } - } - } - $used[$declared] = true; - } - } - - $unused = array_diff_key($deps, $used); - foreach ($unused as $unused_module_key => $ignored) { - $module = $this->getModuleDisplayName($key); - $unused_module = $this->getModuleDisplayName($unused_module_key); - $resolvable[] = $this->raiseLintInModule( - $key, - self::LINT_UNUSED_MODULE, - "Module '{$module}' requires module '{$unused_module}' but does not ". - "use anything it declares.", - $spec['requires']['module'][$unused_module_key]); - $drop_modules[] = $unused_module_key; - } - - foreach ($spec['requires']['source'] as $file => $where) { - if (empty($spec['declares']['source'][$file])) { - $module = $this->getModuleDisplayName($key); - $resolvable[] = $this->raiseLintInModule( - $key, - self::LINT_UNDECLARED_SOURCE, - "Module '{$module}' requires source '{$file}', but it does not ". - "exist.", - $where); - } - } - - foreach ($spec['declares']['source'] as $file => $ignored) { - if (empty($spec['requires']['source'][$file])) { - $module = $this->getModuleDisplayName($key); - $resolvable[] = $this->raiseLintInModule( - $key, - self::LINT_UNUSED_SOURCE, - "Module '{$module}' does not include source file '{$file}'.", - null); - } - } - - if ($resolvable) { - $new_file = $this->buildNewModuleInit( - $key, - $spec, - $need_classes, - $need_functions, - $drop_modules); - $init_path = $this->getModulePathOnDisk($key).'/__init__.php'; - - $root = $this->getEngine()->getWorkingCopy()->getProjectRoot(); - $try_path = Filesystem::readablePath($init_path, $root); - $full_path = Filesystem::resolvePath($try_path, $root); - if (Filesystem::pathExists($full_path)) { - $init_path = $try_path; - $old_file = Filesystem::readFile($full_path); - } else { - $old_file = ''; - } - - $this->willLintPath($init_path); - $message = $this->raiseLintAtOffset( - null, - self::LINT_INIT_REBUILD, - "This generated phutil '__init__.php' file is suggested to address ". - "lint problems with static dependencies in the module.", - $old_file, - $new_file); - $message->setDependentMessages($resolvable); - foreach ($resolvable as $resolvable_message) { - $resolvable_message->setObsolete(true); - } - } - } - - private function buildNewModuleInit( - $key, - $spec, - $need_classes, - $need_functions, - $drop_modules) { - - $init = array(); - $init[] = ' $class_spec) { - $modules[$class_spec['library'].':'.$class_spec['module']] = true; - } - - foreach ($need_functions as $need => $func_spec) { - $modules[$func_spec['library'].':'.$func_spec['module']] = true; - } - - ksort($modules); - - $last = null; - foreach ($modules as $module_key => $ignored) { - - if (is_array($ignored)) { - $in_init = false; - $in_file = false; - foreach ($ignored as $where) { - list($file, $line) = explode(':', $where); - if ($file == '__init__.php') { - $in_init = true; - } else { - $in_file = true; - } - } - if ($in_file && !$in_init) { - // If this is a runtime include, don't try to put it in the - // __init__ file. - continue; - } - } - - list($library, $module_name) = explode(':', $module_key); - if ($last != $library) { - $last = $library; - if ($last != null) { - $init[] = null; - } - } - - $library = "'".addcslashes($library, "'\\")."'"; - $module_name = "'".addcslashes($module_name, "'\\")."'"; - - $init[] = "phutil_require_module({$library}, {$module_name});"; - } - - $init[] = null; - $init[] = null; - - $files = array_keys($spec['declares']['source']); - sort($files); - - foreach ($files as $file) { - $file = "'".addcslashes($file, "'\\")."'"; - $init[] = "phutil_require_source({$file});"; - } - $init[] = null; - - return implode("\n", $init); - } - - private function checkDependency($type, $name, $deps) { - foreach ($deps as $key => $dep) { - if (isset($dep['declares'][$type][$name])) { - return $key; - } - } - return false; - } - - public function raiseLintInModule($key, $code, $desc, $places, $text = null) { - if ($places) { - foreach ($places as $place) { - list($file, $offset) = explode(':', $place); - $this->willLintPath( - Filesystem::readablePath( - $this->getModulePathOnDisk($key).'/'.$file, - $this->getEngine()->getWorkingCopy()->getProjectRoot())); - return $this->raiseLintAtOffset( - $offset, - $code, - $desc, - $text); - } - } else { - $this->willLintPath($this->getModuleDisplayName($key)); - return $this->raiseLintAtPath( - $code, - $desc); - } - } - - public function lintPath($path) { - return; - } - -} diff --git a/src/workflow/ArcanistLiberateWorkflow.php b/src/workflow/ArcanistLiberateWorkflow.php index 868536d1..2fee574f 100644 --- a/src/workflow/ArcanistLiberateWorkflow.php +++ b/src/workflow/ArcanistLiberateWorkflow.php @@ -131,7 +131,9 @@ EOTEXT if ($this->getArgument('upgrade')) { return $this->upgradeLibrary($path); } - return $this->liberateVersion1($path); + throw new ArcanistUsageException( + "This library is using libphutil v1, which is no longer supported. ". + "Run 'arc liberate --upgrade' to upgrade to v2."); case 2: if ($this->getArgument('upgrade')) { throw new ArcanistUsageException( @@ -186,139 +188,6 @@ EOTEXT $this->liberateVersion2($path); } - private function liberateVersion1($path) { - - if ($this->getArgument('remap')) { - return $this->liberateRunRemap($path); - } - - if ($this->getArgument('verify')) { - return $this->liberateRunVerify($path); - } - - $readable = Filesystem::readablePath($path); - echo "Using library root at '{$readable}'...\n"; - - $this->checkForLooseFiles($path); - - if ($this->getArgument('all')) { - echo "Dropping module cache...\n"; - Filesystem::remove($path.'/.phutil_module_cache'); - } - - echo "Mapping library...\n"; - - // Force a rebuild of the library map before running lint. The remap - // operation will load the map before regenerating it, so if a class has - // been renamed (say, from OldClass to NewClass) this rebuild will - // cause the initial remap to see NewClass and correctly remove includes - // caused by use of OldClass. - $this->liberateGetChangedPaths($path); - - $arc_bin = $this->getScriptPath('bin/arc'); - - do { - $future = new ExecFuture( - '%s liberate --remap -- %s', - $arc_bin, - $path); - $wrote = $future->resolveJSON(); - foreach ($wrote as $wrote_path) { - echo "Updated '{$wrote_path}'...\n"; - } - } while ($wrote); - - echo "Verifying library...\n"; - - $err = phutil_passthru('%s liberate --verify -- %s', $arc_bin, $path); - - $do_update = (!$err || $this->getArgument('force-update')); - - if ($do_update) { - echo "Finalizing library map...\n"; - execx('%s %s', $this->getPhutilMapperLocation(), $path); - } - - if ($err) { - if ($do_update) { - echo phutil_console_format( - "** WARNING ** Library update forced, but lint ". - "failures remain.\n"); - } else { - echo phutil_console_format( - "** UNRESOLVED LINT ERRORS ** This library has ". - "unresolved lint failures. The library map was not updated. Use ". - "--force-update to force an update.\n"); - } - } else { - echo phutil_console_format( - "** OKAY ** Library updated.\n"); - } - - return $err; - } - - private function liberateLintModules($path, array $changed) { - $engine = $this->liberateBuildLintEngine($path, $changed); - if ($engine) { - return $engine->run(); - } else { - return array(); - } - } - - private function liberateWritePatches(array $results) { - assert_instances_of($results, 'ArcanistLintResult'); - $wrote = array(); - - foreach ($results as $result) { - if ($result->isPatchable()) { - $patcher = ArcanistLintPatcher::newFromArcanistLintResult($result); - $patcher->writePatchToDisk(); - $wrote[] = $result->getPath(); - } - } - - return $wrote; - } - - private function liberateBuildLintEngine($path, array $changed) { - $lint_map = array(); - foreach ($changed as $module) { - $module_path = $path.'/'.$module; - $files = Filesystem::listDirectory($module_path); - $lint_map[$module] = $files; - } - - $working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile( - $path, - json_encode( - array( - 'project_id' => '__arcliberate__', - )), - 'arc liberate'); - - $engine = new ArcanistLiberateLintEngine(); - $engine->setWorkingCopy($working_copy); - - $lint_paths = array(); - foreach ($lint_map as $module => $files) { - foreach ($files as $file) { - $lint_paths[] = $module.'/'.$file; - } - } - - if (!$lint_paths) { - return null; - } - - $engine->setPaths($lint_paths); - $engine->setMinimumSeverity(ArcanistLintSeverity::SEVERITY_ERROR); - - return $engine; - } - - private function liberateCreateDirectory($path) { if (Filesystem::pathExists($path)) { if (!is_dir($path)) { @@ -361,72 +230,10 @@ EOTEXT Filesystem::writeFile($init_path, $template); } - private function liberateGetChangedPaths($path) { - $mapper = $this->getPhutilMapperLocation(); - $future = new ExecFuture('%s %s --find-paths-for-liberate', $mapper, $path); - return $future->resolveJSON(); - } private function getScriptPath($script) { $root = dirname(phutil_get_library_root('arcanist')); return $root.'/'.$script; } - private function getPhutilMapperLocation() { - return $this->getScriptPath('scripts/phutil_mapper.php'); - } - - private function liberateRunRemap($path) { - phutil_load_library($path); - - $paths = $this->liberateGetChangedPaths($path); - $results = $this->liberateLintModules($path, $paths); - $wrote = $this->liberateWritePatches($results); - - echo json_encode($wrote, true); - - return 0; - } - - private function liberateRunVerify($path) { - phutil_load_library($path); - - $paths = $this->liberateGetChangedPaths($path); - $results = $this->liberateLintModules($path, $paths); - - $renderer = new ArcanistLintConsoleRenderer(); - - $unresolved = false; - foreach ($results as $result) { - foreach ($result->getMessages() as $message) { - echo $renderer->renderLintResult($result); - $unresolved = true; - break; - } - } - - return (int)$unresolved; - } - - /** - * Sanity check to catch people putting class files in the root of a libphutil - * library. - */ - private function checkForLooseFiles($path) { - foreach (Filesystem::listDirectory($path) as $item) { - if (!preg_match('/\.php$/', $item)) { - // Not a ".php" file. - continue; - } - if (preg_match('/^__/', $item)) { - // Has magic '__' prefix. - continue; - } - - echo phutil_console_wrap( - "WARNING: File '{$item}' is not in a module and won't be loaded. ". - "Put source files in subdirectories, not the top level directory.\n"); - } - } - }