mirror of
https://github.com/danog/psalm.git
synced 2024-12-02 09:37:59 +01:00
Reduce PossiblyUnusedParam false positives
This commit is contained in:
parent
c0e923acb9
commit
752e99ad2e
@ -26,7 +26,6 @@ use Psalm\Issue\MixedInferredReturnType;
|
|||||||
use Psalm\Issue\MoreSpecificImplementedReturnType;
|
use Psalm\Issue\MoreSpecificImplementedReturnType;
|
||||||
use Psalm\Issue\MoreSpecificReturnType;
|
use Psalm\Issue\MoreSpecificReturnType;
|
||||||
use Psalm\Issue\OverriddenMethodAccess;
|
use Psalm\Issue\OverriddenMethodAccess;
|
||||||
use Psalm\Issue\PossiblyUnusedParam;
|
|
||||||
use Psalm\Issue\UntypedParam;
|
use Psalm\Issue\UntypedParam;
|
||||||
use Psalm\Issue\UnusedParam;
|
use Psalm\Issue\UnusedParam;
|
||||||
use Psalm\Issue\UnusedVariable;
|
use Psalm\Issue\UnusedVariable;
|
||||||
@ -512,28 +511,48 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
|||||||
$method_name_lc = strtolower((string)$this->function->name);
|
$method_name_lc = strtolower((string)$this->function->name);
|
||||||
$parent_method_id = end($class_storage->overridden_method_ids[$method_name_lc]);
|
$parent_method_id = end($class_storage->overridden_method_ids[$method_name_lc]);
|
||||||
|
|
||||||
|
$position = array_search(substr($var_name, 1), array_keys($storage->param_types), true);
|
||||||
|
|
||||||
|
if ($position === false) {
|
||||||
|
throw new \UnexpectedValueException('$position should not be false here');
|
||||||
|
}
|
||||||
|
|
||||||
if ($parent_method_id) {
|
if ($parent_method_id) {
|
||||||
list($parent_fq_class_name) = explode('::', $parent_method_id);
|
$parent_method_storage = MethodChecker::getStorage($project_checker, $parent_method_id);
|
||||||
|
|
||||||
$parent_method_class_storage = $classlike_storage_provider->get($parent_fq_class_name);
|
// if the parent method has a param at that position and isn't abstract
|
||||||
|
if (!$parent_method_storage->abstract
|
||||||
if (!$parent_method_class_storage->abstract) {
|
&& isset($parent_method_storage->params[$position])
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IssueBuffer::accepts(
|
$storage->unused_params[$position] = $original_location;
|
||||||
new PossiblyUnusedParam(
|
|
||||||
'Param ' . $var_name . ' is never referenced in this method',
|
|
||||||
$original_location
|
|
||||||
),
|
|
||||||
$this->getSuppressedIssues()
|
|
||||||
)) {
|
|
||||||
// fall through
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($storage instanceof MethodStorage && $class_storage) {
|
||||||
|
foreach ($storage->params as $i => $_) {
|
||||||
|
if (!isset($storage->unused_params[$i])) {
|
||||||
|
$storage->used_params[$i] = true;
|
||||||
|
|
||||||
|
/** @var ClassMethod $this->function */
|
||||||
|
$method_name_lc = strtolower((string)$this->function->name);
|
||||||
|
|
||||||
|
if (!isset($class_storage->overridden_method_ids[$method_name_lc])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($class_storage->overridden_method_ids[$method_name_lc] as $parent_method_id) {
|
||||||
|
$parent_method_storage = MethodChecker::getStorage($project_checker, $parent_method_id);
|
||||||
|
|
||||||
|
$parent_method_storage->used_params[$i] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ use Psalm\Exception;
|
|||||||
use Psalm\FileManipulation\FunctionDocblockManipulator;
|
use Psalm\FileManipulation\FunctionDocblockManipulator;
|
||||||
use Psalm\Issue\CircularReference;
|
use Psalm\Issue\CircularReference;
|
||||||
use Psalm\Issue\PossiblyUnusedMethod;
|
use Psalm\Issue\PossiblyUnusedMethod;
|
||||||
|
use Psalm\Issue\PossiblyUnusedParam;
|
||||||
use Psalm\Issue\UnusedClass;
|
use Psalm\Issue\UnusedClass;
|
||||||
use Psalm\Issue\UnusedMethod;
|
use Psalm\Issue\UnusedMethod;
|
||||||
use Psalm\IssueBuffer;
|
use Psalm\IssueBuffer;
|
||||||
@ -1117,7 +1118,7 @@ class ProjectChecker
|
|||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self::checkMethodReferences($classlike_storage);
|
$this->checkMethodReferences($classlike_storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1126,18 +1127,43 @@ class ProjectChecker
|
|||||||
/**
|
/**
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected static function checkMethodReferences(\Psalm\Storage\ClassLikeStorage $classlike_storage)
|
protected function checkMethodReferences(\Psalm\Storage\ClassLikeStorage $classlike_storage)
|
||||||
{
|
{
|
||||||
foreach ($classlike_storage->methods as $method_name => $method_storage) {
|
foreach ($classlike_storage->methods as $method_name => $method_storage) {
|
||||||
if (($method_storage->referencing_locations === null
|
if (($method_storage->referencing_locations === null
|
||||||
|| count($method_storage->referencing_locations) === 0)
|
|| count($method_storage->referencing_locations) === 0)
|
||||||
&& !$classlike_storage->overridden_method_ids[$method_name]
|
|
||||||
&& (substr($method_name, 0, 2) !== '__' || $method_name === '__construct')
|
&& (substr($method_name, 0, 2) !== '__' || $method_name === '__construct')
|
||||||
&& $method_storage->location
|
&& $method_storage->location
|
||||||
) {
|
) {
|
||||||
$method_id = $classlike_storage->name . '::' . $method_storage->cased_name;
|
$method_id = $classlike_storage->name . '::' . $method_storage->cased_name;
|
||||||
|
|
||||||
if ($method_storage->visibility === ClassLikeChecker::VISIBILITY_PUBLIC) {
|
if ($method_storage->visibility === ClassLikeChecker::VISIBILITY_PUBLIC) {
|
||||||
|
$method_name_lc = strtolower($method_name);
|
||||||
|
|
||||||
|
$has_parent_references = false;
|
||||||
|
|
||||||
|
foreach ($classlike_storage->overridden_method_ids[$method_name_lc] as $parent_method_id) {
|
||||||
|
$parent_method_storage = MethodChecker::getStorage($this, $parent_method_id);
|
||||||
|
|
||||||
|
if (!$parent_method_storage->abstract || $parent_method_storage->referencing_locations) {
|
||||||
|
$has_parent_references = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($classlike_storage->class_implements as $fq_interface_name) {
|
||||||
|
$interface_storage = $this->classlike_storage_provider->get($fq_interface_name);
|
||||||
|
if (isset($interface_storage->methods[$method_name])) {
|
||||||
|
$interface_method_storage = $interface_storage->methods[$method_name];
|
||||||
|
|
||||||
|
if ($interface_method_storage->referencing_locations) {
|
||||||
|
$has_parent_references = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$has_parent_references) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new PossiblyUnusedMethod(
|
new PossiblyUnusedMethod(
|
||||||
'Cannot find public calls to method ' . $method_id,
|
'Cannot find public calls to method ' . $method_id,
|
||||||
@ -1147,6 +1173,7 @@ class ProjectChecker
|
|||||||
)) {
|
)) {
|
||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} elseif (!isset($classlike_storage->declaring_method_ids['__call'])) {
|
} elseif (!isset($classlike_storage->declaring_method_ids['__call'])) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new UnusedMethod(
|
new UnusedMethod(
|
||||||
@ -1157,6 +1184,35 @@ class ProjectChecker
|
|||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($method_storage->unused_params as $offset => $code_location) {
|
||||||
|
$has_parent_references = false;
|
||||||
|
|
||||||
|
$method_name_lc = strtolower($method_name);
|
||||||
|
|
||||||
|
foreach ($classlike_storage->overridden_method_ids[$method_name_lc] as $parent_method_id) {
|
||||||
|
$parent_method_storage = MethodChecker::getStorage($this, $parent_method_id);
|
||||||
|
|
||||||
|
if (!$parent_method_storage->abstract
|
||||||
|
&& isset($parent_method_storage->used_params[$offset])
|
||||||
|
) {
|
||||||
|
$has_parent_references = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$has_parent_references && !isset($method_storage->used_params[$offset])) {
|
||||||
|
if (IssueBuffer::accepts(
|
||||||
|
new PossiblyUnusedParam(
|
||||||
|
'Param #' . $offset . ' is never referenced in this method',
|
||||||
|
$code_location
|
||||||
|
),
|
||||||
|
$method_storage->suppressed_issues
|
||||||
|
)) {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Psalm\Storage;
|
namespace Psalm\Storage;
|
||||||
|
|
||||||
|
use Psalm\CodeLocation;
|
||||||
|
|
||||||
class MethodStorage extends FunctionLikeStorage
|
class MethodStorage extends FunctionLikeStorage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -27,4 +29,14 @@ class MethodStorage extends FunctionLikeStorage
|
|||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $final;
|
public $final;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<int, CodeLocation>
|
||||||
|
*/
|
||||||
|
public $unused_params = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<int, bool>
|
||||||
|
*/
|
||||||
|
public $used_params = [];
|
||||||
}
|
}
|
||||||
|
@ -129,3 +129,13 @@ function reset(array &$arr) {}
|
|||||||
* @return TValue|false
|
* @return TValue|false
|
||||||
*/
|
*/
|
||||||
function end(array &$arr) {}
|
function end(array &$arr) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
*
|
||||||
|
* @param mixed $needle
|
||||||
|
* @param array<T, mixed> $haystack
|
||||||
|
* @param bool $strict
|
||||||
|
* @return T|false
|
||||||
|
*/
|
||||||
|
function array_search($needle, array $haystack, bool $strict = false) {}
|
||||||
|
Loading…
Reference in New Issue
Block a user