1
0
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:
Matt Brown 2021-06-12 16:13:29 -04:00
parent 9b17fb94b0
commit 23b7ff86b7
6 changed files with 186 additions and 6 deletions

View File

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

View File

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

View File

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

View File

@ -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 = [];

View File

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

View File

@ -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',
],
];
}
}