diff --git a/assets/config_levels/4.xml b/assets/config_levels/4.xml index 4dae788fc..b78613cbd 100644 --- a/assets/config_levels/4.xml +++ b/assets/config_levels/4.xml @@ -39,6 +39,5 @@ - diff --git a/assets/config_levels/5.xml b/assets/config_levels/5.xml index 28b1447b8..8ab5a5e20 100644 --- a/assets/config_levels/5.xml +++ b/assets/config_levels/5.xml @@ -39,7 +39,6 @@ - diff --git a/config.xsd b/config.xsd index 812e83df4..98bed198d 100644 --- a/config.xsd +++ b/config.xsd @@ -188,8 +188,8 @@ - + @@ -219,6 +219,7 @@ + diff --git a/psalm.xml b/psalm.xml index 9b2c9070a..9c97adff7 100644 --- a/psalm.xml +++ b/psalm.xml @@ -25,12 +25,19 @@ - + - + + + + + + + + @@ -55,6 +62,9 @@ + + + diff --git a/src/Psalm/Checker/ClassLikeChecker.php b/src/Psalm/Checker/ClassLikeChecker.php index ac5dfeafa..33b9cdcd5 100644 --- a/src/Psalm/Checker/ClassLikeChecker.php +++ b/src/Psalm/Checker/ClassLikeChecker.php @@ -1378,33 +1378,6 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc } } - /** - * @param string $class_name - * @param mixed $visibility - * - * @return array - */ - 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 * diff --git a/src/Psalm/Checker/FunctionLikeChecker.php b/src/Psalm/Checker/FunctionLikeChecker.php index c95b612c7..029ed521e 100644 --- a/src/Psalm/Checker/FunctionLikeChecker.php +++ b/src/Psalm/Checker/FunctionLikeChecker.php @@ -26,8 +26,9 @@ use Psalm\Issue\MixedInferredReturnType; use Psalm\Issue\MoreSpecificImplementedReturnType; use Psalm\Issue\MoreSpecificReturnType; use Psalm\Issue\OverriddenMethodAccess; -use Psalm\Issue\PossiblyUnusedVariable; +use Psalm\Issue\PossiblyUnusedParam; use Psalm\Issue\UntypedParam; +use Psalm\Issue\UnusedParam; use Psalm\Issue\UnusedVariable; use Psalm\IssueBuffer; use Psalm\StatementsSource; @@ -129,6 +130,10 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo $implemented_docblock_param_types = []; + $project_checker = $this->file_checker->project_checker; + + $classlike_storage_provider = $project_checker->classlike_storage_provider; + if ($this->function instanceof ClassMethod) { $real_method_id = (string)$this->getMethodId(); @@ -162,10 +167,6 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo $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); $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); if (!isset($context->referenced_var_ids[$var_name]) && $original_location) { - if (!isset($storage->param_types[substr($var_name, 1)]) || - !$storage instanceof MethodStorage || - $storage->visibility === ClassLikeChecker::VISIBILITY_PRIVATE - ) { + if (!array_key_exists(substr($var_name, 1), $storage->param_types)) { if (IssueBuffer::accepts( new UnusedVariable( 'Variable ' . $var_name . ' is never referenced', @@ -486,10 +484,40 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo )) { // fall through } - } else { + } elseif (!$storage instanceof MethodStorage + || $storage->visibility === ClassLikeChecker::VISIBILITY_PRIVATE + ) { if (IssueBuffer::accepts( - new PossiblyUnusedVariable( - 'Variable ' . $var_name . ' is never referenced in this method', + new UnusedParam( + '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 ), $this->getSuppressedIssues() diff --git a/src/Psalm/Checker/ProjectChecker.php b/src/Psalm/Checker/ProjectChecker.php index acc0bbfd4..6b36be74f 100644 --- a/src/Psalm/Checker/ProjectChecker.php +++ b/src/Psalm/Checker/ProjectChecker.php @@ -920,6 +920,8 @@ class ProjectChecker * @param string $file_path * * @return void + * + * @psalm-suppress UnusedParam */ function ($i, $file_path) use ($filetype_handlers) { $file_checker = $this->getFile($file_path, $filetype_handlers, true); @@ -1117,11 +1119,9 @@ class ProjectChecker } /** - * @param \Psalm\Storage\ClassLikeStorage $classlike_storage - * * @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) { if (($method_storage->referencing_locations === null @@ -1137,7 +1137,8 @@ class ProjectChecker new PossiblyUnusedMethod( 'Cannot find public calls to method ' . $method_id, $method_storage->location - ) + ), + $method_storage->suppressed_issues )) { // fall through } diff --git a/src/Psalm/Issue/CodeIssue.php b/src/Psalm/Issue/CodeIssue.php index 9c259c0fe..c99972b15 100644 --- a/src/Psalm/Issue/CodeIssue.php +++ b/src/Psalm/Issue/CodeIssue.php @@ -61,6 +61,8 @@ abstract class CodeIssue /** * @return string + * + * @psalm-suppress PossiblyUnusedMethod for convenience */ public function getFileName() { diff --git a/src/Psalm/Issue/PossiblyUnusedParam.php b/src/Psalm/Issue/PossiblyUnusedParam.php new file mode 100644 index 000000000..d15c38053 --- /dev/null +++ b/src/Psalm/Issue/PossiblyUnusedParam.php @@ -0,0 +1,6 @@ +|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; } diff --git a/src/Psalm/Provider/ParserCacheProvider.php b/src/Psalm/Provider/ParserCacheProvider.php index 8367f5aac..6587330c7 100644 --- a/src/Psalm/Provider/ParserCacheProvider.php +++ b/src/Psalm/Provider/ParserCacheProvider.php @@ -26,7 +26,6 @@ class ParserCacheProvider public $use_igbinary = false; /** - * @param string $file_path * @param string $file_content_hash * @param string $file_cache_key * @param mixed $file_modified_time @@ -35,7 +34,7 @@ class ParserCacheProvider * * @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(); diff --git a/src/Psalm/Provider/StatementsProvider.php b/src/Psalm/Provider/StatementsProvider.php index fef2f026c..3151726a6 100644 --- a/src/Psalm/Provider/StatementsProvider.php +++ b/src/Psalm/Provider/StatementsProvider.php @@ -35,7 +35,6 @@ class StatementsProvider $file_cache_key = $cache_provider->getParserCacheKey($file_path); $stmts = $cache_provider->loadStatementsFromCache( - $file_path, $modified_time, $file_content_hash, $file_cache_key diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 694ba4009..052c4f6fa 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -165,14 +165,6 @@ class Union return isset($this->types['array']); } - /** - * @return bool - */ - public function hasObjectLike() - { - return isset($this->types['array']) && $this->types['array'] instanceof Atomic\ObjectLike; - } - /** * @return bool */ @@ -301,18 +293,6 @@ class Union 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 */ diff --git a/tests/Provider/FakeParserCacheProvider.php b/tests/Provider/FakeParserCacheProvider.php index b0961d413..ae767a4cc 100644 --- a/tests/Provider/FakeParserCacheProvider.php +++ b/tests/Provider/FakeParserCacheProvider.php @@ -6,14 +6,13 @@ use PhpParser; class FakeParserCacheProvider extends \Psalm\Provider\ParserCacheProvider { /** - * @param string $file_path * @param string $file_content_hash * @param string $file_cache_key * @param mixed $file_modified_time * * @return array|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; }