Changeset View
Changeset View
Standalone View
Standalone View
src/lint/linter/ArcanistPhutilLibraryLinter.php
| Show First 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | public function getLintNameMap() { | ||||
| ); | ); | ||||
| } | } | ||||
| public function getLinterPriority() { | public function getLinterPriority() { | ||||
| return 2.0; | return 2.0; | ||||
| } | } | ||||
| public function willLintPaths(array $paths) { | public function willLintPaths(array $paths) { | ||||
| $libtype_map = array( | |||||
| 'class' => 'class', | |||||
| 'function' => 'function', | |||||
| 'interface' => 'class', | |||||
| 'class/interface' => 'class', | |||||
| ); | |||||
| // NOTE: For now, we completely ignore paths and just lint every library in | // 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 | // 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 | // detailed checks and all the data we need for this comes out of module | ||||
| // caches. | // caches. | ||||
| $bootloader = PhutilBootloader::getInstance(); | $bootloader = PhutilBootloader::getInstance(); | ||||
| $libraries = $bootloader->getAllLibraries(); | $libraries = $bootloader->getAllLibraries(); | ||||
| // Load all the builtin symbols first. | |||||
| $builtin_map = PhutilLibraryMapBuilder::newBuiltinMap(); | |||||
| $builtin_map = $builtin_map['have']; | |||||
| $normal_symbols = array(); | |||||
| $all_symbols = array(); | |||||
| foreach ($builtin_map as $type => $builtin_symbols) { | |||||
| $libtype = $libtype_map[$type]; | |||||
| foreach ($builtin_symbols as $builtin_symbol => $ignored) { | |||||
| $normal_symbol = $this->normalizeSymbol($builtin_symbol); | |||||
| $normal_symbols[$type][$normal_symbol] = $builtin_symbol; | |||||
| $all_symbols[$libtype][$builtin_symbol] = array( | |||||
| 'library' => null, | |||||
| 'file' => null, | |||||
| 'offset' => null, | |||||
| ); | |||||
| } | |||||
| } | |||||
| // Load the up-to-date map for each library, without loading the library | // Load the up-to-date map for each library, without loading the library | ||||
| // itself. This means lint results will accurately reflect the state of | // itself. This means lint results will accurately reflect the state of | ||||
| // the working copy. | // the working copy. | ||||
| $symbols = array(); | $symbols = array(); | ||||
| foreach ($libraries as $library) { | foreach ($libraries as $library) { | ||||
| $root = phutil_get_library_root($library); | $root = phutil_get_library_root($library); | ||||
| try { | try { | ||||
| $symbols[$library] = id(new PhutilLibraryMapBuilder($root)) | $symbols[$library] = id(new PhutilLibraryMapBuilder($root)) | ||||
| ->buildFileSymbolMap(); | ->buildFileSymbolMap(); | ||||
| } catch (XHPASTSyntaxErrorException $ex) { | } catch (XHPASTSyntaxErrorException $ex) { | ||||
| // If the library contains a syntax error then there isn't much that we | // If the library contains a syntax error then there isn't much that we | ||||
| // can do. | // can do. | ||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| $all_symbols = array(); | |||||
| foreach ($symbols as $library => $map) { | foreach ($symbols as $library => $map) { | ||||
| // Check for files which declare more than one class/interface in the same | // Check for files which declare more than one class/interface in the same | ||||
| // file, or mix function definitions with class/interface definitions. We | // file, or mix function definitions with class/interface definitions. We | ||||
| // must isolate autoloadable symbols to one per file so the autoloader | // must isolate autoloadable symbols to one per file so the autoloader | ||||
| // can't end up in an unresolvable cycle. | // can't end up in an unresolvable cycle. | ||||
| foreach ($map as $file => $spec) { | foreach ($map as $file => $spec) { | ||||
| $have = idx($spec, 'have', array()); | $have = idx($spec, 'have', array()); | ||||
| Show All 28 Lines | foreach ($symbols as $library => $map) { | ||||
| "File '%s' declares more than one class or interface (%s). ". | "File '%s' declares more than one class or interface (%s). ". | ||||
| "A file which declares a class or interface MUST declare ". | "A file which declares a class or interface MUST declare ". | ||||
| "nothing else.", | "nothing else.", | ||||
| $file, | $file, | ||||
| $class_list)); | $class_list)); | ||||
| } | } | ||||
| } | } | ||||
| $libtype_map = array( | |||||
| 'class' => 'class', | |||||
| 'function' => 'function', | |||||
| 'interface' => 'class', | |||||
| 'class/interface' => 'class', | |||||
| ); | |||||
| // Check for duplicate symbols: two files providing the same class or | // Check for duplicate symbols: two files providing the same class or | ||||
| // function. While doing this, we also build a map of normalized symbol | // function. While doing this, we also build a map of normalized symbol | ||||
| // names to original symbol names: we want a definition of "idx()" to | // names to original symbol names: we want a definition of "idx()" to | ||||
| // collide with a definition of "IdX()", and want to perform spelling | // collide with a definition of "IdX()", and want to perform spelling | ||||
| // corrections later. | // corrections later. | ||||
| $normal_symbols = array(); | |||||
| foreach ($map as $file => $spec) { | foreach ($map as $file => $spec) { | ||||
| $have = idx($spec, 'have', array()); | $have = idx($spec, 'have', array()); | ||||
| foreach (array('class', 'function', 'interface') as $type) { | foreach (array('class', 'function', 'interface') as $type) { | ||||
| $libtype = $libtype_map[$type]; | $libtype = $libtype_map[$type]; | ||||
| foreach (idx($have, $type, array()) as $symbol => $offset) { | foreach (idx($have, $type, array()) as $symbol => $offset) { | ||||
| $normal_symbol = $this->normalizeSymbol($symbol); | $normal_symbol = $this->normalizeSymbol($symbol); | ||||
| if (empty($normal_symbols[$libtype][$normal_symbol])) { | if (empty($normal_symbols[$libtype][$normal_symbol])) { | ||||
| $normal_symbols[$libtype][$normal_symbol] = $symbol; | $normal_symbols[$libtype][$normal_symbol] = $symbol; | ||||
| $all_symbols[$libtype][$symbol] = array( | $all_symbols[$libtype][$symbol] = array( | ||||
| 'library' => $library, | 'library' => $library, | ||||
| 'file' => $file, | 'file' => $file, | ||||
| 'offset' => $offset, | 'offset' => $offset, | ||||
| ); | ); | ||||
| continue; | continue; | ||||
| } | } | ||||
| $old_symbol = $normal_symbols[$libtype][$normal_symbol]; | $old_symbol = $normal_symbols[$libtype][$normal_symbol]; | ||||
| $old_src = $all_symbols[$libtype][$old_symbol]['file']; | $old_src = $all_symbols[$libtype][$old_symbol]['file']; | ||||
| $old_lib = $all_symbols[$libtype][$old_symbol]['library']; | $old_lib = $all_symbols[$libtype][$old_symbol]['library']; | ||||
| $this->raiseLintInLibrary( | // If these values are "null", it means that the symbol is a | ||||
| $library, | // builtin symbol provided by PHP or a PHP extension. | ||||
| if ($old_lib === null) { | |||||
| $message = pht( | |||||
| 'Definition of symbol "%s" (of type "%s") in file "%s" in '. | |||||
| 'library "%s" duplicates builtin definition of the same '. | |||||
| 'symbol.', | |||||
| $symbol, | |||||
| $type, | |||||
| $file, | $file, | ||||
| $offset, | $library); | ||||
| self::LINT_DUPLICATE_SYMBOL, | } else { | ||||
| pht( | $message = pht( | ||||
| 'Definition of symbol "%s" (of type "%s") in file "%s" in '. | 'Definition of symbol "%s" (of type "%s") in file "%s" in '. | ||||
| 'library "%s" duplicates prior definition in file "%s" in '. | 'library "%s" duplicates prior definition in file "%s" in '. | ||||
| 'library "%s".', | 'library "%s".', | ||||
| $symbol, | $symbol, | ||||
| $type, | $type, | ||||
| $file, | $file, | ||||
| $library, | $library, | ||||
| $old_src, | $old_src, | ||||
| $old_lib)); | $old_lib); | ||||
| } | |||||
| $this->raiseLintInLibrary( | |||||
| $library, | |||||
| $file, | |||||
| $offset, | |||||
| self::LINT_DUPLICATE_SYMBOL, | |||||
| $message); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| $types = array('class', 'function', 'interface', 'class/interface'); | $types = array('class', 'function', 'interface', 'class/interface'); | ||||
| foreach ($symbols as $library => $map) { | foreach ($symbols as $library => $map) { | ||||
| // Check for unknown symbols: uses of classes, functions or interfaces | // Check for unknown symbols: uses of classes, functions or interfaces | ||||
| ▲ Show 20 Lines • Show All 150 Lines • Show Last 20 Lines | |||||