1
0
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:
Matthew Brown 2017-12-29 17:27:16 -05:00
parent 8efc939a5f
commit 875bb8c072
16 changed files with 76 additions and 81 deletions

View File

@ -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>

View File

@ -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... -->

View File

@ -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>

View File

@ -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>

View File

@ -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
* *

View File

@ -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()

View File

@ -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
} }

View File

@ -61,6 +61,8 @@ abstract class CodeIssue
/** /**
* @return string * @return string
*
* @psalm-suppress PossiblyUnusedMethod for convenience
*/ */
public function getFileName() public function getFileName()
{ {

View File

@ -0,0 +1,6 @@
<?php
namespace Psalm\Issue;
class PossiblyUnusedParam extends CodeError
{
}

View File

@ -1,6 +0,0 @@
<?php
namespace Psalm\Issue;
class PossiblyUnusedVariable extends CodeError
{
}

View File

@ -0,0 +1,6 @@
<?php
namespace Psalm\Issue;
class UnusedParam extends CodeError
{
}

View File

@ -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;
} }

View File

@ -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();

View File

@ -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

View File

@ -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
*/ */

View File

@ -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;
} }