mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Mark interface methods as unused even when inherited
This commit is contained in:
parent
9b17fb94b0
commit
23b7ff86b7
@ -64,10 +64,9 @@ class MethodComparator
|
||||
|
||||
$cased_guide_method_id = $guide_classlike_storage->name . '::' . $guide_method_storage->cased_name;
|
||||
|
||||
$codebase->methods->file_reference_provider->addMethodReferenceToClassMember(
|
||||
$codebase->methods->file_reference_provider->addMethodDependencyToClassMember(
|
||||
strtolower((string)($implementer_declaring_method_id ?: $implementer_method_id)),
|
||||
strtolower($guide_classlike_storage->name . '::' . $guide_method_storage->cased_name),
|
||||
true
|
||||
strtolower($guide_classlike_storage->name . '::' . $guide_method_storage->cased_name)
|
||||
);
|
||||
|
||||
self::checkForObviousMethodMismatches(
|
||||
|
@ -56,6 +56,7 @@ use const PATHINFO_EXTENSION;
|
||||
* function_timings: array<string, float>,
|
||||
* file_manipulations: array<string, FileManipulation[]>,
|
||||
* method_references_to_class_members: array<string, array<string,bool>>,
|
||||
* method_dependencies: array<string, array<string,bool>>,
|
||||
* method_references_to_method_returns: array<string, array<string,bool>>,
|
||||
* method_references_to_class_properties: array<string, array<string,bool>>,
|
||||
* method_references_to_missing_class_members: array<string, array<string,bool>>,
|
||||
@ -449,6 +450,7 @@ class Analyzer
|
||||
'method_references_to_classes' => $file_reference_provider->getAllMethodReferencesToClasses(),
|
||||
'file_references_to_class_members' => $file_reference_provider->getAllFileReferencesToClassMembers(),
|
||||
'method_references_to_class_members' => $file_reference_provider->getAllMethodReferencesToClassMembers(),
|
||||
'method_dependencies' => $file_reference_provider->getAllMethodDependencies(),
|
||||
'file_references_to_class_properties' => $file_reference_provider->getAllFileReferencesToClassProperties(),
|
||||
'file_references_to_method_returns' => $file_reference_provider->getAllFileReferencesToMethodReturns(),
|
||||
'method_references_to_class_properties' => $file_reference_provider->getAllMethodReferencesToClassProperties(),
|
||||
@ -518,6 +520,9 @@ class Analyzer
|
||||
$codebase->file_reference_provider->addMethodReferencesToClassMembers(
|
||||
$pool_data['method_references_to_class_members']
|
||||
);
|
||||
$codebase->file_reference_provider->addMethodDependencies(
|
||||
$pool_data['method_dependencies']
|
||||
);
|
||||
$codebase->file_reference_provider->addMethodReferencesToClassProperties(
|
||||
$pool_data['method_references_to_class_properties']
|
||||
);
|
||||
@ -641,6 +646,8 @@ class Analyzer
|
||||
|
||||
$method_references_to_class_members = $file_reference_provider->getAllMethodReferencesToClassMembers();
|
||||
|
||||
$method_dependencies = $file_reference_provider->getAllMethodDependencies();
|
||||
|
||||
$method_references_to_class_properties = $file_reference_provider->getAllMethodReferencesToClassProperties();
|
||||
|
||||
$method_references_to_method_returns = $file_reference_provider->getAllMethodReferencesToMethodReturns();
|
||||
@ -648,7 +655,9 @@ class Analyzer
|
||||
$method_references_to_missing_class_members =
|
||||
$file_reference_provider->getAllMethodReferencesToMissingClassMembers();
|
||||
|
||||
$all_referencing_methods = $method_references_to_class_members + $method_references_to_missing_class_members;
|
||||
$all_referencing_methods = $method_references_to_class_members
|
||||
+ $method_references_to_missing_class_members
|
||||
+ $method_dependencies;
|
||||
|
||||
$nonmethod_references_to_classes = $file_reference_provider->getAllNonMethodReferencesToClasses();
|
||||
|
||||
@ -742,6 +751,7 @@ class Analyzer
|
||||
|
||||
unset(
|
||||
$method_references_to_class_members[$member_id],
|
||||
$method_dependencies[$member_id],
|
||||
$method_references_to_class_properties[$member_id],
|
||||
$method_references_to_method_returns[$member_id],
|
||||
$file_references_to_class_members[$member_id],
|
||||
@ -769,6 +779,10 @@ class Analyzer
|
||||
unset($method_references_to_class_members[$i][$method_id]);
|
||||
}
|
||||
|
||||
foreach ($method_dependencies as $i => $_) {
|
||||
unset($method_dependencies[$i][$method_id]);
|
||||
}
|
||||
|
||||
foreach ($method_references_to_class_properties as $i => $_) {
|
||||
unset($method_references_to_class_properties[$i][$method_id]);
|
||||
}
|
||||
@ -865,6 +879,10 @@ class Analyzer
|
||||
$method_references_to_class_members
|
||||
);
|
||||
|
||||
$method_dependencies = array_filter(
|
||||
$method_dependencies
|
||||
);
|
||||
|
||||
$method_references_to_class_properties = array_filter(
|
||||
$method_references_to_class_properties
|
||||
);
|
||||
@ -913,6 +931,10 @@ class Analyzer
|
||||
$method_references_to_class_members
|
||||
);
|
||||
|
||||
$file_reference_provider->setMethodDependencies(
|
||||
$method_dependencies
|
||||
);
|
||||
|
||||
$file_reference_provider->setCallingMethodReferencesToClassProperties(
|
||||
$method_references_to_class_properties
|
||||
);
|
||||
|
@ -27,6 +27,7 @@ class FileReferenceCacheProvider
|
||||
private const METHOD_CLASS_REFERENCE_CACHE_NAME = 'method_class_references';
|
||||
private const ANALYZED_METHODS_CACHE_NAME = 'analyzed_methods';
|
||||
private const CLASS_METHOD_CACHE_NAME = 'class_method_references';
|
||||
private const METHOD_DEPENDENCIES_CACHE_NAME = 'class_method_dependencies';
|
||||
private const CLASS_PROPERTY_CACHE_NAME = 'class_property_references';
|
||||
private const CLASS_METHOD_RETURN_CACHE_NAME = 'class_method_return_references';
|
||||
private const FILE_METHOD_RETURN_CACHE_NAME = 'file_method_return_references';
|
||||
@ -188,6 +189,33 @@ class FileReferenceCacheProvider
|
||||
return $class_member_reference_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MixedAssignment
|
||||
*/
|
||||
public function getCachedMethodDependencies(): ?array
|
||||
{
|
||||
$cache_directory = $this->config->getCacheDirectory();
|
||||
|
||||
if (!$cache_directory) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$method_dependencies_cache_location
|
||||
= $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_DEPENDENCIES_CACHE_NAME;
|
||||
|
||||
if (!is_readable($method_dependencies_cache_location)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$method_dependencies_cache = unserialize((string) file_get_contents($method_dependencies_cache_location));
|
||||
|
||||
if (!is_array($method_dependencies_cache)) {
|
||||
throw new \UnexpectedValueException('The reference cache must be an array');
|
||||
}
|
||||
|
||||
return $method_dependencies_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MixedAssignment
|
||||
*/
|
||||
@ -518,6 +546,19 @@ class FileReferenceCacheProvider
|
||||
file_put_contents($member_cache_location, serialize($member_references));
|
||||
}
|
||||
|
||||
public function setCachedMethodDependencies(array $member_references): void
|
||||
{
|
||||
$cache_directory = $this->config->getCacheDirectory();
|
||||
|
||||
if (!$cache_directory) {
|
||||
return;
|
||||
}
|
||||
|
||||
$member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_DEPENDENCIES_CACHE_NAME;
|
||||
|
||||
file_put_contents($member_cache_location, serialize($member_references));
|
||||
}
|
||||
|
||||
public function setCachedMethodPropertyReferences(array $property_references): void
|
||||
{
|
||||
$cache_directory = $this->config->getCacheDirectory();
|
||||
|
@ -92,6 +92,11 @@ class FileReferenceProvider
|
||||
*/
|
||||
private static $method_references_to_class_members = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, bool>>
|
||||
*/
|
||||
private static $method_dependencies = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, bool>>
|
||||
*/
|
||||
@ -461,6 +466,14 @@ class FileReferenceProvider
|
||||
return self::$method_references_to_class_members;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, bool>>
|
||||
*/
|
||||
public function getAllMethodDependencies(): array
|
||||
{
|
||||
return self::$method_dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, bool>>
|
||||
*/
|
||||
@ -549,6 +562,14 @@ class FileReferenceProvider
|
||||
|
||||
self::$method_references_to_class_members = $method_references_to_class_members;
|
||||
|
||||
$method_dependencies = $this->cache->getCachedMethodDependencies();
|
||||
|
||||
if ($method_dependencies === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self::$method_dependencies = $method_dependencies;
|
||||
|
||||
$method_references_to_class_properties = $this->cache->getCachedMethodPropertyReferences();
|
||||
|
||||
if ($method_references_to_class_properties === null) {
|
||||
@ -693,6 +714,7 @@ class FileReferenceProvider
|
||||
$this->cache->setCachedMethodClassReferences(self::$method_references_to_classes);
|
||||
$this->cache->setCachedNonMethodClassReferences(self::$nonmethod_references_to_classes);
|
||||
$this->cache->setCachedMethodMemberReferences(self::$method_references_to_class_members);
|
||||
$this->cache->setCachedMethodDependencies(self::$method_dependencies);
|
||||
$this->cache->setCachedMethodPropertyReferences(self::$method_references_to_class_properties);
|
||||
$this->cache->setCachedMethodMethodReturnReferences(self::$method_references_to_method_returns);
|
||||
$this->cache->setCachedFileMemberReferences(self::$file_references_to_class_members);
|
||||
@ -742,6 +764,17 @@ class FileReferenceProvider
|
||||
}
|
||||
}
|
||||
|
||||
public function addMethodDependencyToClassMember(
|
||||
string $calling_function_id,
|
||||
string $referenced_member_id
|
||||
): void {
|
||||
if (!isset(self::$method_dependencies[$referenced_member_id])) {
|
||||
self::$method_dependencies[$referenced_member_id] = [$calling_function_id => true];
|
||||
} else {
|
||||
self::$method_dependencies[$referenced_member_id][$calling_function_id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function addMethodReferenceToClassProperty(string $calling_function_id, string $referenced_property_id): void
|
||||
{
|
||||
if (!isset(self::$method_references_to_class_properties[$referenced_property_id])) {
|
||||
@ -895,6 +928,24 @@ class FileReferenceProvider
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string,bool>> $references
|
||||
*
|
||||
*/
|
||||
public function addMethodDependencies(array $references): void
|
||||
{
|
||||
foreach ($references as $key => $reference) {
|
||||
if (isset(self::$method_dependencies[$key])) {
|
||||
self::$method_dependencies[$key] = array_merge(
|
||||
$reference,
|
||||
self::$method_dependencies[$key]
|
||||
);
|
||||
} else {
|
||||
self::$method_dependencies[$key] = $reference;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string,bool>> $references
|
||||
*
|
||||
@ -1009,6 +1060,15 @@ class FileReferenceProvider
|
||||
self::$method_references_to_class_members = $references;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string,bool>> $references
|
||||
*
|
||||
*/
|
||||
public function setMethodDependencies(array $references): void
|
||||
{
|
||||
self::$method_dependencies = $references;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string,bool>> $references
|
||||
*
|
||||
@ -1235,6 +1295,7 @@ class FileReferenceProvider
|
||||
self::$file_references_to_class_properties = [];
|
||||
self::$file_references_to_method_returns = [];
|
||||
self::$method_references_to_class_members = [];
|
||||
self::$method_dependencies = [];
|
||||
self::$method_references_to_class_properties = [];
|
||||
self::$method_references_to_method_returns = [];
|
||||
self::$method_references_to_classes = [];
|
||||
|
@ -22,6 +22,9 @@ class FakeFileReferenceCacheProvider extends \Psalm\Internal\Provider\FileRefere
|
||||
/** @var ?array */
|
||||
private $cached_method_member_references;
|
||||
|
||||
/** @var ?array */
|
||||
private $cached_method_dependencies;
|
||||
|
||||
/** @var ?array */
|
||||
private $cached_method_property_references;
|
||||
|
||||
@ -112,6 +115,11 @@ class FakeFileReferenceCacheProvider extends \Psalm\Internal\Provider\FileRefere
|
||||
return $this->cached_method_member_references;
|
||||
}
|
||||
|
||||
public function getCachedMethodDependencies(): ?array
|
||||
{
|
||||
return $this->cached_method_dependencies;
|
||||
}
|
||||
|
||||
public function getCachedMethodPropertyReferences(): ?array
|
||||
{
|
||||
return $this->cached_method_property_references;
|
||||
@ -172,6 +180,11 @@ class FakeFileReferenceCacheProvider extends \Psalm\Internal\Provider\FileRefere
|
||||
$this->cached_method_member_references = $member_references;
|
||||
}
|
||||
|
||||
public function setCachedMethodDependencies(array $member_references): void
|
||||
{
|
||||
$this->cached_method_dependencies = $member_references;
|
||||
}
|
||||
|
||||
public function setCachedMethodPropertyReferences(array $property_references): void
|
||||
{
|
||||
$this->cached_method_property_references = $property_references;
|
||||
|
@ -962,7 +962,7 @@ class UnusedCodeTest extends TestCase
|
||||
|
||||
(new A())->foo()->bar();',
|
||||
],
|
||||
'SKIPPED-unusedInterfaceReturnValueWithImplementingClassSuppressed' => [
|
||||
'unusedInterfaceReturnValueWithImplementingClassSuppressed' => [
|
||||
'<?php
|
||||
interface IWorker {
|
||||
/** @psalm-suppress PossiblyUnusedReturnValue */
|
||||
@ -981,6 +981,33 @@ class UnusedCodeTest extends TestCase
|
||||
|
||||
f(new Worker());',
|
||||
],
|
||||
'interfaceReturnValueWithImplementingAndAbstractClass' => [
|
||||
'<?php
|
||||
interface IWorker {
|
||||
public function work(): int;
|
||||
}
|
||||
|
||||
class AbstractWorker implements IWorker {
|
||||
public function work(): int {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Worker extends AbstractWorker {
|
||||
public function work(): int {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
class AnotherWorker extends AbstractWorker {}
|
||||
|
||||
function f(IWorker $worker): void {
|
||||
echo $worker->work();
|
||||
}
|
||||
|
||||
f(new Worker());
|
||||
f(new AnotherWorker());',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -1370,7 +1397,7 @@ class UnusedCodeTest extends TestCase
|
||||
}',
|
||||
'error_message' => 'PossiblyUnusedReturnValue',
|
||||
],
|
||||
'SKIPPED-unusedInterfaceReturnValueWithImplementingClass' => [
|
||||
'unusedInterfaceReturnValueWithImplementingClass' => [
|
||||
'<?php
|
||||
interface IWorker {
|
||||
public function work(): bool;
|
||||
@ -1404,6 +1431,23 @@ class UnusedCodeTest extends TestCase
|
||||
(new A())->foo()->bar();',
|
||||
'error_message' => 'PossiblyUnusedReturnValue',
|
||||
],
|
||||
'interfaceWithImplementingClassMethodUnused' => [
|
||||
'<?php
|
||||
interface IWorker {
|
||||
public function work(): void;
|
||||
}
|
||||
|
||||
class Worker implements IWorker {
|
||||
public function work(): void {}
|
||||
}
|
||||
|
||||
function f(IWorker $worker): void {
|
||||
echo get_class($worker);
|
||||
}
|
||||
|
||||
f(new Worker());',
|
||||
'error_message' => 'PossiblyUnusedMethod',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user