mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Improve dead code detection, reducing false positives for params
This commit is contained in:
parent
8efc939a5f
commit
875bb8c072
@ -39,6 +39,5 @@
|
|||||||
<PossiblyNullPropertyFetch errorLevel="info" />
|
<PossiblyNullPropertyFetch errorLevel="info" />
|
||||||
<PossiblyNullReference errorLevel="info" />
|
<PossiblyNullReference errorLevel="info" />
|
||||||
<PossiblyUndefinedVariable errorLevel="info" />
|
<PossiblyUndefinedVariable errorLevel="info" />
|
||||||
<PossiblyUnusedVariable errorLevel="info" />
|
|
||||||
</issueHandlers>
|
</issueHandlers>
|
||||||
</psalm>
|
</psalm>
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
<PossiblyNullPropertyFetch errorLevel="info" />
|
<PossiblyNullPropertyFetch errorLevel="info" />
|
||||||
<PossiblyNullReference errorLevel="info" />
|
<PossiblyNullReference errorLevel="info" />
|
||||||
<PossiblyUndefinedVariable errorLevel="info" />
|
<PossiblyUndefinedVariable errorLevel="info" />
|
||||||
<PossiblyUnusedVariable errorLevel="info" />
|
|
||||||
|
|
||||||
<!-- level 5 issues - should be avoided at mosts costs... -->
|
<!-- level 5 issues - should be avoided at mosts costs... -->
|
||||||
|
|
||||||
|
@ -188,8 +188,8 @@
|
|||||||
<xs:element name="PossiblyUndefinedMethod" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="PossiblyUndefinedMethod" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="PossiblyUndefinedGlobalVariable" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="PossiblyUndefinedGlobalVariable" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="PossiblyUndefinedVariable" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="PossiblyUndefinedVariable" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="PossiblyUnusedVariable" type="IssueHandlerType" minOccurs="0" />
|
|
||||||
<xs:element name="PossiblyUnusedMethod" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="PossiblyUnusedMethod" type="IssueHandlerType" minOccurs="0" />
|
||||||
|
<xs:element name="PossiblyUnusedParam" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="PropertyNotSetInConstructor" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="PropertyNotSetInConstructor" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="RawObjectIteration" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="RawObjectIteration" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="RedundantCondition" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="RedundantCondition" type="IssueHandlerType" minOccurs="0" />
|
||||||
@ -219,6 +219,7 @@
|
|||||||
<xs:element name="UnresolvableInclude" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="UnresolvableInclude" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="UnevaluatedCode" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="UnevaluatedCode" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="UnusedVariable" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="UnusedVariable" type="IssueHandlerType" minOccurs="0" />
|
||||||
|
<xs:element name="UnusedParam" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="UnusedClass" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="UnusedClass" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="UnusedMethod" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="UnusedMethod" type="IssueHandlerType" minOccurs="0" />
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
|
14
psalm.xml
14
psalm.xml
@ -25,12 +25,19 @@
|
|||||||
<MissingConstructor errorLevel="suppress" />
|
<MissingConstructor errorLevel="suppress" />
|
||||||
<DeprecatedProperty errorLevel="suppress" />
|
<DeprecatedProperty errorLevel="suppress" />
|
||||||
|
|
||||||
<PossiblyUnusedVariable>
|
<UnusedParam>
|
||||||
<errorLevel type="suppress">
|
<errorLevel type="suppress">
|
||||||
<file name="src/Psalm/Plugin.php" />
|
<file name="src/Psalm/Plugin.php" />
|
||||||
<directory name="examples" />
|
<directory name="examples" />
|
||||||
</errorLevel>
|
</errorLevel>
|
||||||
</PossiblyUnusedVariable>
|
</UnusedParam>
|
||||||
|
|
||||||
|
<PossiblyUnusedParam>
|
||||||
|
<errorLevel type="suppress">
|
||||||
|
<file name="src/Psalm/Plugin.php" />
|
||||||
|
<directory name="examples" />
|
||||||
|
</errorLevel>
|
||||||
|
</PossiblyUnusedParam>
|
||||||
|
|
||||||
<UnusedVariable>
|
<UnusedVariable>
|
||||||
<errorLevel type="suppress">
|
<errorLevel type="suppress">
|
||||||
@ -55,6 +62,9 @@
|
|||||||
<errorLevel type="suppress">
|
<errorLevel type="suppress">
|
||||||
<directory name="tests" />
|
<directory name="tests" />
|
||||||
<directory name="examples" />
|
<directory name="examples" />
|
||||||
|
<file name="src/Psalm/Type/Atomic/GenericTrait.php" />
|
||||||
|
<file name="src/Psalm/TraitSource.php" />
|
||||||
|
<file name="src/Psalm/FileManipulation/FileManipulationBuffer.php" />
|
||||||
</errorLevel>
|
</errorLevel>
|
||||||
</PossiblyUnusedMethod>
|
</PossiblyUnusedMethod>
|
||||||
|
|
||||||
|
@ -1378,33 +1378,6 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $class_name
|
|
||||||
* @param mixed $visibility
|
|
||||||
*
|
|
||||||
* @return array<string, PropertyStorage>
|
|
||||||
*/
|
|
||||||
public static function getPropertiesForClass(ProjectChecker $project_checker, $class_name, $visibility)
|
|
||||||
{
|
|
||||||
$storage = $project_checker->classlike_storage_provider->get($class_name);
|
|
||||||
|
|
||||||
$properties = [];
|
|
||||||
|
|
||||||
foreach ($storage->properties as $property_name => $property) {
|
|
||||||
if (!$property->is_static) {
|
|
||||||
if ($visibility === ReflectionProperty::IS_PRIVATE ||
|
|
||||||
$property->visibility === ClassLikeChecker::VISIBILITY_PUBLIC ||
|
|
||||||
($property->visibility === ClassLikeChecker::VISIBILITY_PROTECTED &&
|
|
||||||
$visibility === ReflectionProperty::IS_PROTECTED)
|
|
||||||
) {
|
|
||||||
$properties[$property_name] = $property;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Psalm type from a particular value
|
* Gets the Psalm type from a particular value
|
||||||
*
|
*
|
||||||
|
@ -26,8 +26,9 @@ 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\PossiblyUnusedVariable;
|
use Psalm\Issue\PossiblyUnusedParam;
|
||||||
use Psalm\Issue\UntypedParam;
|
use Psalm\Issue\UntypedParam;
|
||||||
|
use Psalm\Issue\UnusedParam;
|
||||||
use Psalm\Issue\UnusedVariable;
|
use Psalm\Issue\UnusedVariable;
|
||||||
use Psalm\IssueBuffer;
|
use Psalm\IssueBuffer;
|
||||||
use Psalm\StatementsSource;
|
use Psalm\StatementsSource;
|
||||||
@ -129,6 +130,10 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
|||||||
|
|
||||||
$implemented_docblock_param_types = [];
|
$implemented_docblock_param_types = [];
|
||||||
|
|
||||||
|
$project_checker = $this->file_checker->project_checker;
|
||||||
|
|
||||||
|
$classlike_storage_provider = $project_checker->classlike_storage_provider;
|
||||||
|
|
||||||
if ($this->function instanceof ClassMethod) {
|
if ($this->function instanceof ClassMethod) {
|
||||||
$real_method_id = (string)$this->getMethodId();
|
$real_method_id = (string)$this->getMethodId();
|
||||||
|
|
||||||
@ -162,10 +167,6 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
|||||||
|
|
||||||
$fq_class_name = (string)$context->self;
|
$fq_class_name = (string)$context->self;
|
||||||
|
|
||||||
$project_checker = $this->file_checker->project_checker;
|
|
||||||
|
|
||||||
$classlike_storage_provider = $project_checker->classlike_storage_provider;
|
|
||||||
|
|
||||||
$class_storage = $classlike_storage_provider->get($fq_class_name);
|
$class_storage = $classlike_storage_provider->get($fq_class_name);
|
||||||
|
|
||||||
$storage = MethodChecker::getStorage($project_checker, $declaring_method_id);
|
$storage = MethodChecker::getStorage($project_checker, $declaring_method_id);
|
||||||
@ -473,10 +474,7 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
|||||||
$original_location = $statements_checker->getFirstAppearance($var_name);
|
$original_location = $statements_checker->getFirstAppearance($var_name);
|
||||||
|
|
||||||
if (!isset($context->referenced_var_ids[$var_name]) && $original_location) {
|
if (!isset($context->referenced_var_ids[$var_name]) && $original_location) {
|
||||||
if (!isset($storage->param_types[substr($var_name, 1)]) ||
|
if (!array_key_exists(substr($var_name, 1), $storage->param_types)) {
|
||||||
!$storage instanceof MethodStorage ||
|
|
||||||
$storage->visibility === ClassLikeChecker::VISIBILITY_PRIVATE
|
|
||||||
) {
|
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new UnusedVariable(
|
new UnusedVariable(
|
||||||
'Variable ' . $var_name . ' is never referenced',
|
'Variable ' . $var_name . ' is never referenced',
|
||||||
@ -486,10 +484,40 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
|||||||
)) {
|
)) {
|
||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
} else {
|
} elseif (!$storage instanceof MethodStorage
|
||||||
|
|| $storage->visibility === ClassLikeChecker::VISIBILITY_PRIVATE
|
||||||
|
) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new PossiblyUnusedVariable(
|
new UnusedParam(
|
||||||
'Variable ' . $var_name . ' is never referenced in this method',
|
'Param ' . $var_name . ' is never referenced in this method',
|
||||||
|
$original_location
|
||||||
|
),
|
||||||
|
$this->getSuppressedIssues()
|
||||||
|
)) {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!$class_storage || $storage->abstract) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var ClassMethod $this->function */
|
||||||
|
$method_name_lc = strtolower((string)$this->function->name);
|
||||||
|
$parent_method_id = end($class_storage->overridden_method_ids[$method_name_lc]);
|
||||||
|
|
||||||
|
if ($parent_method_id) {
|
||||||
|
list($parent_fq_class_name) = explode('::', $parent_method_id);
|
||||||
|
|
||||||
|
$parent_method_class_storage = $classlike_storage_provider->get($parent_fq_class_name);
|
||||||
|
|
||||||
|
if (!$parent_method_class_storage->abstract) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IssueBuffer::accepts(
|
||||||
|
new PossiblyUnusedParam(
|
||||||
|
'Param ' . $var_name . ' is never referenced in this method',
|
||||||
$original_location
|
$original_location
|
||||||
),
|
),
|
||||||
$this->getSuppressedIssues()
|
$this->getSuppressedIssues()
|
||||||
|
@ -920,6 +920,8 @@ class ProjectChecker
|
|||||||
* @param string $file_path
|
* @param string $file_path
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
|
*
|
||||||
|
* @psalm-suppress UnusedParam
|
||||||
*/
|
*/
|
||||||
function ($i, $file_path) use ($filetype_handlers) {
|
function ($i, $file_path) use ($filetype_handlers) {
|
||||||
$file_checker = $this->getFile($file_path, $filetype_handlers, true);
|
$file_checker = $this->getFile($file_path, $filetype_handlers, true);
|
||||||
@ -1117,11 +1119,9 @@ class ProjectChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Psalm\Storage\ClassLikeStorage $classlike_storage
|
|
||||||
*
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected static function checkMethodReferences($classlike_storage)
|
protected static 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
|
||||||
@ -1137,7 +1137,8 @@ class ProjectChecker
|
|||||||
new PossiblyUnusedMethod(
|
new PossiblyUnusedMethod(
|
||||||
'Cannot find public calls to method ' . $method_id,
|
'Cannot find public calls to method ' . $method_id,
|
||||||
$method_storage->location
|
$method_storage->location
|
||||||
)
|
),
|
||||||
|
$method_storage->suppressed_issues
|
||||||
)) {
|
)) {
|
||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,8 @@ abstract class CodeIssue
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
|
*
|
||||||
|
* @psalm-suppress PossiblyUnusedMethod for convenience
|
||||||
*/
|
*/
|
||||||
public function getFileName()
|
public function getFileName()
|
||||||
{
|
{
|
||||||
|
6
src/Psalm/Issue/PossiblyUnusedParam.php
Normal file
6
src/Psalm/Issue/PossiblyUnusedParam.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Psalm\Issue;
|
||||||
|
|
||||||
|
class PossiblyUnusedParam extends CodeError
|
||||||
|
{
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Psalm\Issue;
|
|
||||||
|
|
||||||
class PossiblyUnusedVariable extends CodeError
|
|
||||||
{
|
|
||||||
}
|
|
6
src/Psalm/Issue/UnusedParam.php
Normal file
6
src/Psalm/Issue/UnusedParam.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Psalm\Issue;
|
||||||
|
|
||||||
|
class UnusedParam extends CodeError
|
||||||
|
{
|
||||||
|
}
|
@ -6,14 +6,13 @@ use PhpParser;
|
|||||||
class NoParserCacheProvider extends \Psalm\Provider\ParserCacheProvider
|
class NoParserCacheProvider extends \Psalm\Provider\ParserCacheProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param string $file_path
|
|
||||||
* @param string $file_content_hash
|
* @param string $file_content_hash
|
||||||
* @param string $file_cache_key
|
* @param string $file_cache_key
|
||||||
* @param mixed $file_modified_time
|
* @param mixed $file_modified_time
|
||||||
*
|
*
|
||||||
* @return array<int, PhpParser\Node\Stmt>|null
|
* @return array<int, PhpParser\Node\Stmt>|null
|
||||||
*/
|
*/
|
||||||
public function loadStatementsFromCache($file_path, $file_modified_time, $file_content_hash, $file_cache_key)
|
public function loadStatementsFromCache($file_modified_time, $file_content_hash, $file_cache_key)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ class ParserCacheProvider
|
|||||||
public $use_igbinary = false;
|
public $use_igbinary = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $file_path
|
|
||||||
* @param string $file_content_hash
|
* @param string $file_content_hash
|
||||||
* @param string $file_cache_key
|
* @param string $file_cache_key
|
||||||
* @param mixed $file_modified_time
|
* @param mixed $file_modified_time
|
||||||
@ -35,7 +34,7 @@ class ParserCacheProvider
|
|||||||
*
|
*
|
||||||
* @psalm-suppress UndefinedFunction
|
* @psalm-suppress UndefinedFunction
|
||||||
*/
|
*/
|
||||||
public function loadStatementsFromCache($file_path, $file_modified_time, $file_content_hash, $file_cache_key)
|
public function loadStatementsFromCache($file_modified_time, $file_content_hash, $file_cache_key)
|
||||||
{
|
{
|
||||||
$root_cache_directory = Config::getInstance()->getCacheDirectory();
|
$root_cache_directory = Config::getInstance()->getCacheDirectory();
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ class StatementsProvider
|
|||||||
$file_cache_key = $cache_provider->getParserCacheKey($file_path);
|
$file_cache_key = $cache_provider->getParserCacheKey($file_path);
|
||||||
|
|
||||||
$stmts = $cache_provider->loadStatementsFromCache(
|
$stmts = $cache_provider->loadStatementsFromCache(
|
||||||
$file_path,
|
|
||||||
$modified_time,
|
$modified_time,
|
||||||
$file_content_hash,
|
$file_content_hash,
|
||||||
$file_cache_key
|
$file_cache_key
|
||||||
|
@ -165,14 +165,6 @@ class Union
|
|||||||
return isset($this->types['array']);
|
return isset($this->types['array']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasObjectLike()
|
|
||||||
{
|
|
||||||
return isset($this->types['array']) && $this->types['array'] instanceof Atomic\ObjectLike;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
@ -301,18 +293,6 @@ class Union
|
|||||||
return isset($this->types['empty']);
|
return isset($this->types['empty']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function removeObjects()
|
|
||||||
{
|
|
||||||
foreach ($this->types as $key => $type) {
|
|
||||||
if ($type instanceof Atomic\TNamedObject) {
|
|
||||||
unset($this->types[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
|
@ -6,14 +6,13 @@ use PhpParser;
|
|||||||
class FakeParserCacheProvider extends \Psalm\Provider\ParserCacheProvider
|
class FakeParserCacheProvider extends \Psalm\Provider\ParserCacheProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param string $file_path
|
|
||||||
* @param string $file_content_hash
|
* @param string $file_content_hash
|
||||||
* @param string $file_cache_key
|
* @param string $file_cache_key
|
||||||
* @param mixed $file_modified_time
|
* @param mixed $file_modified_time
|
||||||
*
|
*
|
||||||
* @return array<int, PhpParser\Node\Stmt>|null
|
* @return array<int, PhpParser\Node\Stmt>|null
|
||||||
*/
|
*/
|
||||||
public function loadStatementsFromCache($file_path, $file_modified_time, $file_content_hash, $file_cache_key)
|
public function loadStatementsFromCache($file_modified_time, $file_content_hash, $file_cache_key)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user