mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Add way to find all references to a given class or method in the codebase
This commit is contained in:
parent
bb7f8e320c
commit
d9433c9491
10
bin/psalm
10
bin/psalm
@ -24,7 +24,7 @@ $options = getopt(
|
||||
[
|
||||
'help', 'debug', 'config:', 'monochrome', 'show-info:', 'diff',
|
||||
'file:', 'self-check', 'update-docblocks', 'output-format:',
|
||||
'find-dead-code', 'init',
|
||||
'find-dead-code', 'init', 'find-references-to:',
|
||||
]
|
||||
);
|
||||
|
||||
@ -88,6 +88,9 @@ Options:
|
||||
--find-dead-code
|
||||
Look for dead code
|
||||
|
||||
--find-references-to=[class|method]
|
||||
Searches the codebase for references to the given fully-qualified class or method,
|
||||
where method is in the format class::methodName
|
||||
|
||||
HELP;
|
||||
|
||||
@ -237,6 +240,8 @@ $is_diff = isset($options['diff']);
|
||||
|
||||
$find_dead_code = isset($options['find-dead-code']);
|
||||
|
||||
$find_references_to = isset($options['find-references-to']) ? $options['find-references-to'] : null;
|
||||
|
||||
$update_docblocks = isset($options['update-docblocks']);
|
||||
|
||||
$project_checker = new ProjectChecker(
|
||||
@ -245,7 +250,8 @@ $project_checker = new ProjectChecker(
|
||||
$output_format,
|
||||
$debug,
|
||||
$update_docblocks,
|
||||
$find_dead_code
|
||||
$find_dead_code || $find_references_to !== null,
|
||||
$find_references_to
|
||||
);
|
||||
|
||||
// initialise custom config, if passed
|
||||
|
@ -407,7 +407,10 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
}
|
||||
}
|
||||
|
||||
if ($context->collect_references && $context->check_variables) {
|
||||
if ($context->collect_references &&
|
||||
!$this->getFileChecker()->project_checker->find_references_to &&
|
||||
$context->check_variables
|
||||
) {
|
||||
foreach ($context->vars_possibly_in_scope as $var_name => $_) {
|
||||
if (strpos($var_name, '->') === false &&
|
||||
$var_name !== '$this' &&
|
||||
|
@ -54,6 +54,11 @@ class ProjectChecker
|
||||
*/
|
||||
public $collect_references = false;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $find_references_to;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
@ -176,7 +181,8 @@ class ProjectChecker
|
||||
* @param boolean $debug_output
|
||||
* @param string $output_format
|
||||
* @param bool $update_docblocks
|
||||
* @param bool $find_dead_code
|
||||
* @param bool $collect_references
|
||||
* @param string $find_references_to
|
||||
*/
|
||||
public function __construct(
|
||||
$use_color = true,
|
||||
@ -184,13 +190,15 @@ class ProjectChecker
|
||||
$output_format = self::TYPE_CONSOLE,
|
||||
$debug_output = false,
|
||||
$update_docblocks = false,
|
||||
$find_dead_code = false
|
||||
$collect_references = false,
|
||||
$find_references_to = null
|
||||
) {
|
||||
$this->use_color = $use_color;
|
||||
$this->show_info = $show_info;
|
||||
$this->debug_output = $debug_output;
|
||||
$this->update_docblocks = $update_docblocks;
|
||||
$this->collect_references = $find_dead_code;
|
||||
$this->collect_references = $collect_references;
|
||||
$this->find_references_to = $find_references_to;
|
||||
|
||||
if (!in_array($output_format, [self::TYPE_CONSOLE, self::TYPE_JSON, self::TYPE_EMACS])) {
|
||||
throw new \UnexpectedValueException('Unrecognised output format ' . $output_format);
|
||||
@ -279,7 +287,31 @@ class ProjectChecker
|
||||
}
|
||||
|
||||
if ($this->collect_references) {
|
||||
$this->checkClassReferences();
|
||||
if ($this->find_references_to) {
|
||||
if (strpos($this->find_references_to, '::') !== false) {
|
||||
$locations = $this->findReferencesToMethod($this->find_references_to);
|
||||
} else {
|
||||
$locations = $this->findReferencesToClassLike($this->find_references_to);
|
||||
}
|
||||
|
||||
foreach ($locations as $location) {
|
||||
$snippet = $location->getSnippet();
|
||||
|
||||
$snippet_bounds = $location->getSnippetBounds();
|
||||
$selection_bounds = $location->getSelectionBounds();
|
||||
|
||||
$selection_start = $selection_bounds[0] - $snippet_bounds[0];
|
||||
$selection_length = $selection_bounds[1] - $selection_bounds[0];
|
||||
|
||||
echo $location->file_name . ':' . $location->getLineNumber() . PHP_EOL .
|
||||
($this->use_color
|
||||
? substr($snippet, 0, $selection_start) .
|
||||
"\e[97;42m" . substr($snippet, $selection_start, $selection_length) .
|
||||
"\e[0m" . substr($snippet, $selection_length + $selection_start)
|
||||
: $snippet
|
||||
) . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IssueBuffer::finish(true, (int)$start_checks, $this->visited_files);
|
||||
@ -325,6 +357,52 @@ class ProjectChecker
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method_id
|
||||
* @return \Psalm\CodeLocation[]
|
||||
*/
|
||||
public function findReferencesToMethod($method_id)
|
||||
{
|
||||
list($fq_class_name, $method_name) = explode('::', $method_id);
|
||||
|
||||
if (!isset(ClassLikeChecker::$storage[strtolower($fq_class_name)])) {
|
||||
die('Class ' . $fq_class_name . ' cannot be found' . PHP_EOL);
|
||||
}
|
||||
|
||||
$class_storage = ClassLikeChecker::$storage[strtolower($fq_class_name)];
|
||||
|
||||
if (!isset($class_storage->methods[strtolower($method_name)])) {
|
||||
die('Method ' . $method_id . ' cannot be found' . PHP_EOL);
|
||||
}
|
||||
|
||||
$method_storage = $class_storage->methods[strtolower($method_name)];
|
||||
|
||||
if ($method_storage->referencing_locations === null) {
|
||||
die('No references found for ' . $method_id . PHP_EOL);
|
||||
}
|
||||
|
||||
return $method_storage->referencing_locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fq_class_name
|
||||
* @return \Psalm\CodeLocation[]
|
||||
*/
|
||||
public function findReferencesToClassLike($fq_class_name)
|
||||
{
|
||||
if (!isset(ClassLikeChecker::$storage[strtolower($fq_class_name)])) {
|
||||
die('Class ' . $fq_class_name . ' cannot be found' . PHP_EOL);
|
||||
}
|
||||
|
||||
$class_storage = ClassLikeChecker::$storage[strtolower($fq_class_name)];
|
||||
|
||||
if ($class_storage->referencing_locations === null) {
|
||||
die('No references found for ' . $fq_class_name . PHP_EOL);
|
||||
}
|
||||
|
||||
return $class_storage->referencing_locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
@ -697,12 +697,16 @@ class CallChecker
|
||||
continue;
|
||||
}
|
||||
|
||||
$does_class_exist = ClassLikeChecker::checkFullyQualifiedClassLikeName(
|
||||
$fq_class_name,
|
||||
$statements_checker->getFileChecker(),
|
||||
new CodeLocation($statements_checker->getSource(), $stmt->var),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
);
|
||||
if ($var_id === '$this') {
|
||||
$does_class_exist = true;
|
||||
} else {
|
||||
$does_class_exist = ClassLikeChecker::checkFullyQualifiedClassLikeName(
|
||||
$fq_class_name,
|
||||
$statements_checker->getFileChecker(),
|
||||
new CodeLocation($statements_checker->getSource(), $stmt->var),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
);
|
||||
}
|
||||
|
||||
if (!$does_class_exist) {
|
||||
return $does_class_exist;
|
||||
|
Loading…
Reference in New Issue
Block a user