mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-25 16:22:42 +01:00
Verify classes used in typehints
Summary: Also support `SomeInterface::CONSTANT`. Test Plan: interface I { const A = 1; } I::A; function f(stdClass $a, array $b, Iterator $c) { } Linted Phabricator. Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Differential Revision: https://secure.phabricator.com/D6135
This commit is contained in:
parent
e93726cb3b
commit
5336f4bcf0
3 changed files with 35 additions and 16 deletions
|
@ -21,7 +21,7 @@ final class PhutilLibraryMapBuilder {
|
||||||
const LIBRARY_MAP_VERSION = 2;
|
const LIBRARY_MAP_VERSION = 2;
|
||||||
|
|
||||||
const SYMBOL_CACHE_VERSION_KEY = '__symbol_cache_version__';
|
const SYMBOL_CACHE_VERSION_KEY = '__symbol_cache_version__';
|
||||||
const SYMBOL_CACHE_VERSION = 8;
|
const SYMBOL_CACHE_VERSION = 9;
|
||||||
|
|
||||||
|
|
||||||
/* -( Mapping libphutil Libraries )---------------------------------------- */
|
/* -( Mapping libphutil Libraries )---------------------------------------- */
|
||||||
|
|
|
@ -211,10 +211,10 @@ foreach ($classes as $class) {
|
||||||
// - Static method call
|
// - Static method call
|
||||||
// - Static property access
|
// - Static property access
|
||||||
// - Use of class constant
|
// - Use of class constant
|
||||||
|
// - typehints
|
||||||
//
|
//
|
||||||
// TODO: Possibly support these:
|
// TODO: Possibly support these:
|
||||||
//
|
//
|
||||||
// - typehints
|
|
||||||
// - instanceof
|
// - instanceof
|
||||||
// - catch
|
// - catch
|
||||||
// - String literal in ReflectionClass().
|
// - String literal in ReflectionClass().
|
||||||
|
@ -273,11 +273,24 @@ foreach ($static_uses as $static_use) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$need[] = array(
|
$need[] = array(
|
||||||
'type' => 'class',
|
'type' => 'class/interface',
|
||||||
'symbol' => $name,
|
'symbol' => $name,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is "function (X $x)".
|
||||||
|
$parameters = $root->selectDescendantsOfType('n_DECLARATION_PARAMETER');
|
||||||
|
foreach ($parameters as $parameter) {
|
||||||
|
$hint = $parameter->getChildByIndex(0);
|
||||||
|
if ($hint->getTypeName() != 'n_CLASS_NAME') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$need[] = array(
|
||||||
|
'type' => 'class/interface',
|
||||||
|
'symbol' => $hint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -( Interfaces )------------------------------------------------------------
|
// -( Interfaces )------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -355,14 +368,20 @@ foreach ($need as $key => $spec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$type = $spec['type'];
|
$type = $spec['type'];
|
||||||
|
foreach (explode('/', $type) as $libtype) {
|
||||||
if (!$show_all) {
|
if (!$show_all) {
|
||||||
if (!empty($externals[$type][$name])) {
|
if (!empty($externals[$libtype][$name])) {
|
||||||
// Ignore symbols declared as externals.
|
// Ignore symbols declared as externals.
|
||||||
continue;
|
continue 2;
|
||||||
}
|
}
|
||||||
if (!empty($builtins[$type][$name])) {
|
if (!empty($builtins[$libtype][$name])) {
|
||||||
// Ignore symbols declared as builtins.
|
// Ignore symbols declared as builtins.
|
||||||
continue;
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($declared_symbols[$libtype][$name])) {
|
||||||
|
// We declare this symbol, so don't treat it as a requirement.
|
||||||
|
continue 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!empty($required_symbols[$type][$name])) {
|
if (!empty($required_symbols[$type][$name])) {
|
||||||
|
@ -370,10 +389,6 @@ foreach ($need as $key => $spec) {
|
||||||
// isn't terribly informative.
|
// isn't terribly informative.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!empty($declared_symbols[$type][$name])) {
|
|
||||||
// We declare this symbol, so don't treat it as a requirement.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$required_symbols[$type][$name] = $spec['symbol']->getOffset();
|
$required_symbols[$type][$name] = $spec['symbol']->getOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -134,14 +134,18 @@ final class ArcanistPhutilLibraryLinter extends ArcanistLinter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$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
|
||||||
// which are not defined anywhere. We reference the list of all symbols
|
// which are not defined anywhere. We reference the list of all symbols
|
||||||
// we built up earlier.
|
// we built up earlier.
|
||||||
foreach ($map as $file => $spec) {
|
foreach ($map as $file => $spec) {
|
||||||
$need = idx($spec, 'need', array());
|
$need = idx($spec, 'need', array());
|
||||||
foreach (array('class', 'function', 'interface') as $type) {
|
foreach ($types as $type) {
|
||||||
$libtype = ($type == 'interface') ? 'class' : $type;
|
$libtype = $type;
|
||||||
|
if ($type == 'interface' || $type == 'class/interface') {
|
||||||
|
$libtype = 'class';
|
||||||
|
}
|
||||||
foreach (idx($need, $type, array()) as $symbol => $offset) {
|
foreach (idx($need, $type, array()) as $symbol => $offset) {
|
||||||
if (!empty($all_symbols[$libtype][$symbol])) {
|
if (!empty($all_symbols[$libtype][$symbol])) {
|
||||||
// Symbol is defined somewhere.
|
// Symbol is defined somewhere.
|
||||||
|
|
Loading…
Reference in a new issue