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); } } }