From 2c70a1a72aa40cb066dcf23abf05c435d6dcaf4e Mon Sep 17 00:00:00 2001 From: Tomasz Mlynski Date: Tue, 14 Dec 2021 20:00:17 +0100 Subject: [PATCH 01/78] Use flag instead of `DirectoryIterator::isDot()` --- src/Psalm/Config/FileFilter.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Config/FileFilter.php b/src/Psalm/Config/FileFilter.php index 71f04699853..beddce22bc9 100644 --- a/src/Psalm/Config/FileFilter.php +++ b/src/Psalm/Config/FileFilter.php @@ -1,6 +1,7 @@ rewind(); while ($iterator->valid()) { - if (!$iterator->isDot() && $iterator->isLink()) { + if ($iterator->isLink()) { $linked_path = readlink($iterator->getPathname()); if (stripos($linked_path, $directory_path) !== 0) { From 1da689345106293165bb8c6373816265aa043f26 Mon Sep 17 00:00:00 2001 From: Tomasz Mlynski Date: Tue, 14 Dec 2021 20:25:27 +0100 Subject: [PATCH 02/78] Add `resolveSymlinks` condition for project directories --- config.xsd | 1 + src/Psalm/Config/FileFilter.php | 42 ++++++++++++++++++++------------- tests/Config/ConfigTest.php | 2 +- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/config.xsd b/config.xsd index ab37ca2dcd3..cc51ba82ca3 100644 --- a/config.xsd +++ b/config.xsd @@ -144,6 +144,7 @@ + diff --git a/src/Psalm/Config/FileFilter.php b/src/Psalm/Config/FileFilter.php index beddce22bc9..dc9acf742e2 100644 --- a/src/Psalm/Config/FileFilter.php +++ b/src/Psalm/Config/FileFilter.php @@ -114,6 +114,7 @@ public static function loadFromArray( $ignore_type_stats = strtolower( isset($directory['ignoreTypeStats']) ? (string) $directory['ignoreTypeStats'] : '' ) === 'true'; + $resolve_symlinks = isset($directory['resolveSymlinks']) && $directory['resolveSymlinks'] === true; $declare_strict_types = strtolower( isset($directory['useStrictTypes']) ? (string) $directory['useStrictTypes'] : '' ) === 'true'; @@ -186,29 +187,33 @@ public static function loadFromArray( ); } - /** @var RecursiveDirectoryIterator */ - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($directory_path, FilesystemIterator::SKIP_DOTS) - ); - $iterator->rewind(); + if ($resolve_symlinks) { + /** @var RecursiveDirectoryIterator */ + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($directory_path, FilesystemIterator::SKIP_DOTS) + ); + $iterator->rewind(); - while ($iterator->valid()) { - if ($iterator->isLink()) { - $linked_path = readlink($iterator->getPathname()); + while ($iterator->valid()) { + if ($iterator->isLink()) { + $linked_path = readlink($iterator->getPathname()); - if (stripos($linked_path, $directory_path) !== 0) { - if ($ignore_type_stats && $filter instanceof ProjectFileFilter) { - $filter->ignore_type_stats[$directory_path] = true; - } + if (stripos($linked_path, $directory_path) !== 0) { + if ($ignore_type_stats && $filter instanceof ProjectFileFilter) { + $filter->ignore_type_stats[$directory_path] = true; + } - if ($declare_strict_types && $filter instanceof ProjectFileFilter) { - $filter->declare_strict_types[$directory_path] = true; - } + if ($declare_strict_types && $filter instanceof ProjectFileFilter) { + $filter->declare_strict_types[$directory_path] = true; + } - if (is_dir($linked_path)) { - $filter->addDirectory($linked_path); + if (is_dir($linked_path)) { + $filter->addDirectory($linked_path); + } } } + + $iterator->next(); } $iterator->next(); @@ -356,6 +361,9 @@ public static function loadFromXMLElement( isset($directory['ignoreTypeStats']) ? (string) $directory['ignoreTypeStats'] : '' ) === 'true', + 'resolveSymlinks' => strtolower( + isset($directory['resolveSymlinks']) ? (string) $directory['resolveSymlinks'] : '' + ) === 'true', 'useStrictTypes' => strtolower( isset($directory['useStrictTypes']) ? (string) $directory['useStrictTypes'] : '' ) === 'true', diff --git a/tests/Config/ConfigTest.php b/tests/Config/ConfigTest.php index b77e573cf5e..f86dc722807 100644 --- a/tests/Config/ConfigTest.php +++ b/tests/Config/ConfigTest.php @@ -180,7 +180,7 @@ public function testIgnoreSymlinkedProjectDirectory(): void - + ' From 8d6b781b3e5cdf297edfd6b2ea318ae43015a44c Mon Sep 17 00:00:00 2001 From: orklah Date: Fri, 12 Nov 2021 02:29:17 +0100 Subject: [PATCH 03/78] use consistent way to compare php version --- examples/TemplateScanner.php | 2 +- src/Psalm/Codebase.php | 32 ++++++------ src/Psalm/Config.php | 8 +-- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 8 ++- .../FunctionLike/ReturnTypeAnalyzer.php | 7 ++- .../Analyzer/FunctionLikeAnalyzer.php | 5 +- .../Internal/Analyzer/MethodComparator.php | 14 ++---- .../Internal/Analyzer/ProjectAnalyzer.php | 12 ++--- .../Statements/Expression/ArrayAnalyzer.php | 8 +-- .../Expression/Call/ArgumentAnalyzer.php | 8 +-- .../Call/FunctionCallReturnTypeFetcher.php | 5 +- .../Method/MethodCallReturnTypeFetcher.php | 5 +- .../ExistingAtomicStaticCallAnalyzer.php | 5 +- .../Statements/Expression/CastAnalyzer.php | 2 +- .../Statements/ExpressionAnalyzer.php | 12 ++--- src/Psalm/Internal/Codebase/ClassLikes.php | 5 +- .../Codebase/InternalCallMapHandler.php | 4 +- .../Reflector/ClassLikeDocblockParser.php | 2 +- .../Reflector/ClassLikeNodeScanner.php | 3 +- .../Reflector/ExpressionResolver.php | 2 +- .../Reflector/FunctionLikeNodeScanner.php | 25 +++++----- .../PhpVisitor/Reflector/TypeHintResolver.php | 8 ++- .../Internal/PhpVisitor/ReflectorVisitor.php | 2 +- .../Internal/Provider/StatementsProvider.php | 26 ++++++---- src/Psalm/Internal/Scanner/FileScanner.php | 2 +- .../Stubs/Generator/StubsGenerator.php | 2 +- .../Type/Comparator/AtomicTypeComparator.php | 2 +- src/Psalm/Internal/Type/TypeParser.php | 23 +++++---- src/Psalm/Internal/Type/TypeTokenizer.php | 11 ++--- src/Psalm/Type.php | 4 +- src/Psalm/Type/Atomic.php | 49 +++++++------------ src/Psalm/Type/Atomic/CallableTrait.php | 3 +- src/Psalm/Type/Atomic/Scalar.php | 2 +- .../Type/Atomic/TAnonymousClassInstance.php | 7 +-- src/Psalm/Type/Atomic/TArray.php | 5 +- src/Psalm/Type/Atomic/TArrayKey.php | 5 +- src/Psalm/Type/Atomic/TAssertionFalsy.php | 5 +- src/Psalm/Type/Atomic/TBool.php | 5 +- src/Psalm/Type/Atomic/TCallable.php | 5 +- src/Psalm/Type/Atomic/TCallableObject.php | 9 ++-- src/Psalm/Type/Atomic/TCallableString.php | 2 +- src/Psalm/Type/Atomic/TClassConstant.php | 5 +- src/Psalm/Type/Atomic/TClassString.php | 5 +- src/Psalm/Type/Atomic/TClassStringMap.php | 5 +- src/Psalm/Type/Atomic/TClosedResource.php | 5 +- src/Psalm/Type/Atomic/TClosure.php | 2 +- src/Psalm/Type/Atomic/TConditional.php | 5 +- src/Psalm/Type/Atomic/TDependentGetClass.php | 2 +- .../Type/Atomic/TDependentGetDebugType.php | 2 +- src/Psalm/Type/Atomic/TDependentGetType.php | 2 +- src/Psalm/Type/Atomic/TDependentListKey.php | 2 +- src/Psalm/Type/Atomic/TEmpty.php | 3 +- src/Psalm/Type/Atomic/TEnumCase.php | 5 +- src/Psalm/Type/Atomic/TFalse.php | 2 +- src/Psalm/Type/Atomic/TFloat.php | 5 +- src/Psalm/Type/Atomic/TGenericObject.php | 11 ++--- src/Psalm/Type/Atomic/THtmlEscapedString.php | 2 +- src/Psalm/Type/Atomic/TInt.php | 5 +- src/Psalm/Type/Atomic/TIntMask.php | 2 +- src/Psalm/Type/Atomic/TIntMaskOf.php | 2 +- src/Psalm/Type/Atomic/TIntRange.php | 2 +- src/Psalm/Type/Atomic/TIterable.php | 10 ++-- src/Psalm/Type/Atomic/TKeyOfClassConstant.php | 5 +- src/Psalm/Type/Atomic/TKeyedArray.php | 5 +- src/Psalm/Type/Atomic/TList.php | 5 +- src/Psalm/Type/Atomic/TLiteralClassString.php | 5 +- src/Psalm/Type/Atomic/TLowercaseString.php | 2 +- src/Psalm/Type/Atomic/TMixed.php | 9 ++-- src/Psalm/Type/Atomic/TNamedObject.php | 17 +++---- src/Psalm/Type/Atomic/TNever.php | 5 +- .../Type/Atomic/TNonEmptyLowercaseString.php | 2 +- .../Type/Atomic/TNonspecificLiteralInt.php | 2 +- .../Type/Atomic/TNonspecificLiteralString.php | 2 +- src/Psalm/Type/Atomic/TNull.php | 5 +- src/Psalm/Type/Atomic/TNumeric.php | 5 +- src/Psalm/Type/Atomic/TNumericString.php | 2 +- src/Psalm/Type/Atomic/TObject.php | 10 ++-- .../Type/Atomic/TObjectWithProperties.php | 5 +- src/Psalm/Type/Atomic/TPositiveInt.php | 2 +- src/Psalm/Type/Atomic/TResource.php | 5 +- src/Psalm/Type/Atomic/TScalar.php | 5 +- src/Psalm/Type/Atomic/TString.php | 5 +- .../Type/Atomic/TTemplateIndexedAccess.php | 5 +- src/Psalm/Type/Atomic/TTemplateParam.php | 5 +- src/Psalm/Type/Atomic/TTraitString.php | 5 +- src/Psalm/Type/Atomic/TTrue.php | 2 +- src/Psalm/Type/Atomic/TTypeAlias.php | 5 +- .../Type/Atomic/TValueOfClassConstant.php | 5 +- src/Psalm/Type/Atomic/TVoid.php | 9 ++-- src/Psalm/Type/Union.php | 24 +++++---- tests/AlgebraTest.php | 2 +- .../Hook/StringProvider/TSqlSelectString.php | 2 +- tests/FileDiffTest.php | 10 ++-- 93 files changed, 264 insertions(+), 348 deletions(-) diff --git a/examples/TemplateScanner.php b/examples/TemplateScanner.php index f713dfcc8ac..4775758e325 100644 --- a/examples/TemplateScanner.php +++ b/examples/TemplateScanner.php @@ -25,7 +25,7 @@ public function scan( ): void { $stmts = $codebase->statements_provider->getStatementsForFile( $file_storage->file_path, - '7.4', + 70400, $progress ); diff --git a/src/Psalm/Codebase.php b/src/Psalm/Codebase.php index cb72efa8b76..633ebf2d651 100644 --- a/src/Psalm/Codebase.php +++ b/src/Psalm/Codebase.php @@ -77,6 +77,7 @@ use function explode; use function implode; use function in_array; +use function intdiv; use function is_numeric; use function is_string; use function krsort; @@ -90,8 +91,6 @@ use function substr; use function substr_count; -use const PHP_MAJOR_VERSION; -use const PHP_MINOR_VERSION; use const PHP_VERSION_ID; class Codebase @@ -305,18 +304,6 @@ class Codebase */ public $allow_backwards_incompatible_changes = true; - /** - * @var int - * @deprecated Removed in Psalm 5, use Codebase::$analysis_php_version_id - */ - public $php_major_version = PHP_MAJOR_VERSION; - - /** - * @var int - * @deprecated Removed in Psalm 5, use Codebase::$analysis_php_version_id - */ - public $php_minor_version = PHP_MINOR_VERSION; - /** @var int */ public $analysis_php_version_id = PHP_VERSION_ID; @@ -534,7 +521,7 @@ public function getStatementsForFile(string $file_path): array { return $this->statements_provider->getStatementsForFile( $file_path, - $this->php_major_version . '.' . $this->php_minor_version, + $this->analysis_php_version_id, $this->progress ); } @@ -2010,4 +1997,19 @@ public function addTaintSink( $this->taint_flow_graph->addSink($sink); } + + public function getMinorAnalysisPhpVersion(): int + { + return self::transformPhpVersionId($this->analysis_php_version_id % 10000, 100); + } + + public function getMajorAnalysisPhpVersion(): int + { + return self::transformPhpVersionId($this->analysis_php_version_id, 10000); + } + + public static function transformPhpVersionId(int $php_version_id, int $div): int + { + return intdiv($php_version_id, $div); + } } diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 9584302113e..0f725769ad0 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -1871,7 +1871,7 @@ public function visitPreloadedStubFiles(Codebase $codebase, ?Progress $progress $core_generic_files = []; - if (PHP_VERSION_ID < 80000 && $codebase->php_major_version >= 8) { + if (PHP_VERSION_ID < 80000 && $codebase->analysis_php_version_id >= 80000) { $stringable_path = dirname(__DIR__, 2) . '/stubs/Php80.phpstub'; if (!file_exists($stringable_path)) { @@ -1881,7 +1881,7 @@ public function visitPreloadedStubFiles(Codebase $codebase, ?Progress $progress $core_generic_files[] = $stringable_path; } - if (PHP_VERSION_ID < 80100 && $codebase->php_major_version >= 8 && $codebase->php_minor_version >= 1) { + if (PHP_VERSION_ID < 80100 && $codebase->analysis_php_version_id >= 80100) { $stringable_path = dirname(__DIR__, 2) . '/stubs/Php81.phpstub'; if (!file_exists($stringable_path)) { @@ -1932,12 +1932,12 @@ public function visitStubFiles(Codebase $codebase, ?Progress $progress = null): $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'SPL.phpstub', ]; - if (PHP_VERSION_ID >= 80000 && $codebase->php_major_version >= 8) { + if (PHP_VERSION_ID >= 80000 && $codebase->analysis_php_version_id >= 80000) { $stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php80.phpstub'; $this->internal_stubs[] = $stringable_path; } - if (PHP_VERSION_ID >= 80100 && $codebase->php_major_version >= 8 && $codebase->php_minor_version >= 1) { + if (PHP_VERSION_ID >= 80100 && $codebase->analysis_php_version_id >= 80100) { $stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php81.phpstub'; $this->internal_stubs[] = $stringable_path; } diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 2e21a870e59..b4800c82f69 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1608,8 +1608,7 @@ private static function addOrUpdatePropertyType( $codebase = $project_analyzer->getCodebase(); $allow_native_type = !$docblock_only - && $codebase->php_major_version >= 7 - && ($codebase->php_major_version > 7 || $codebase->php_minor_version >= 4) + && $codebase->analysis_php_version_id >= 70400 && $codebase->allow_backwards_incompatible_changes; $manipulator->setType( @@ -1618,8 +1617,7 @@ private static function addOrUpdatePropertyType( $source->getNamespace(), $source->getAliasedClassesFlipped(), $source->getFQCLN(), - $codebase->php_major_version, - $codebase->php_minor_version + $codebase->analysis_php_version_id ) : null, $inferred_type->toNamespacedString( $source->getNamespace(), @@ -1633,7 +1631,7 @@ private static function addOrUpdatePropertyType( $source->getFQCLN(), true ), - $inferred_type->canBeFullyExpressedInPhp($codebase->php_major_version, $codebase->php_minor_version) + $inferred_type->canBeFullyExpressedInPhp($codebase->analysis_php_version_id) ); } diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php index 2566009bcba..e8722b1bfdc 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php @@ -942,7 +942,7 @@ private static function addOrUpdateReturnType( } $allow_native_type = !$docblock_only - && $codebase->php_major_version >= 7 + && $codebase->analysis_php_version_id >= 70000 && ( $codebase->allow_backwards_incompatible_changes || $is_final @@ -955,8 +955,7 @@ private static function addOrUpdateReturnType( $source->getNamespace(), $source->getAliasedClassesFlipped(), $source->getFQCLN(), - $codebase->php_major_version, - $codebase->php_minor_version + $codebase->analysis_php_version_id ) : null, $inferred_return_type->toNamespacedString( $source->getNamespace(), @@ -970,7 +969,7 @@ private static function addOrUpdateReturnType( $source->getFQCLN(), true ), - $inferred_return_type->canBeFullyExpressedInPhp($codebase->php_major_version, $codebase->php_minor_version), + $inferred_return_type->canBeFullyExpressedInPhp($codebase->analysis_php_version_id), $function_like_storage->return_type_description ?? null ); } diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 99a11cbb337..11beca2d23d 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -1443,7 +1443,7 @@ public function addOrUpdateParamType( } $allow_native_type = !$docblock_only - && $codebase->php_major_version >= 7 + && $codebase->analysis_php_version_id >= 70000 && ( $codebase->allow_backwards_incompatible_changes || $is_final @@ -1457,8 +1457,7 @@ public function addOrUpdateParamType( $this->source->getNamespace(), $this->source->getAliasedClassesFlipped(), $this->source->getFQCLN(), - $project_analyzer->getCodebase()->php_major_version, - $project_analyzer->getCodebase()->php_minor_version + $project_analyzer->getCodebase()->analysis_php_version_id ) : null, $inferred_return_type->toNamespacedString( $this->source->getNamespace(), diff --git a/src/Psalm/Internal/Analyzer/MethodComparator.php b/src/Psalm/Internal/Analyzer/MethodComparator.php index 16ff2d9d98b..1f26cbd43b8 100644 --- a/src/Psalm/Internal/Analyzer/MethodComparator.php +++ b/src/Psalm/Internal/Analyzer/MethodComparator.php @@ -92,7 +92,7 @@ public static function compare( $cased_implementer_method_id, $prevent_method_signature_mismatch, $prevent_abstract_override, - $codebase->php_major_version >= 8, + $codebase->analysis_php_version_id >= 80000, $code_location, $suppressed_issues ); @@ -559,9 +559,7 @@ private static function compareMethodSignatureParams( $implementer_classlike_storage->parent_class ); - $is_contained_by = (($codebase->php_major_version === 7 - && $codebase->php_minor_version === 4) - || $codebase->php_major_version >= 8) + $is_contained_by = $codebase->analysis_php_version_id >= 70400 && $guide_param_signature_type ? UnionTypeComparator::isContainedBy( $codebase, @@ -575,7 +573,7 @@ private static function compareMethodSignatureParams( if (!$is_contained_by) { $config = Config::getInstance(); - if ($codebase->php_major_version >= 8 + if ($codebase->analysis_php_version_id >= 80000 || $guide_classlike_storage->is_trait === $implementer_classlike_storage->is_trait || !in_array($guide_classlike_storage->name, $implementer_classlike_storage->used_traits) || $implementer_method_storage->defining_fqcln !== $implementer_classlike_storage->name @@ -853,9 +851,7 @@ private static function compareMethodSignatureReturnTypes( $implementer_classlike_storage->parent_class ) : null; - $is_contained_by = (($codebase->php_major_version === 7 - && $codebase->php_minor_version === 4) - || $codebase->php_major_version >= 8) + $is_contained_by = $codebase->analysis_php_version_id >= 70400 && $implementer_signature_return_type ? UnionTypeComparator::isContainedBy( $codebase, @@ -865,7 +861,7 @@ private static function compareMethodSignatureReturnTypes( : UnionTypeComparator::isContainedByInPhp($implementer_signature_return_type, $guide_signature_return_type); if (!$is_contained_by) { - if ($codebase->php_major_version >= 8 + if ($codebase->analysis_php_version_id >= 80000 || $guide_classlike_storage->is_trait === $implementer_classlike_storage->is_trait || !in_array($guide_classlike_storage->name, $implementer_classlike_storage->used_traits) || $implementer_method_storage->defining_fqcln !== $implementer_classlike_storage->name diff --git a/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php b/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php index 4e99005e19e..abe4d6e562b 100644 --- a/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php @@ -569,7 +569,7 @@ private function generatePHPVersionMessage(): string { $codebase = $this->codebase; - $version = $codebase->php_major_version . '.' . $codebase->php_minor_version; + $version = $codebase->getMajorAnalysisPhpVersion() . '.' . $codebase->getMinorAnalysisPhpVersion(); switch ($codebase->php_version_source) { case 'cli': @@ -1297,17 +1297,15 @@ public function setPhpVersion(string $version, string $source): void $php_major_version = (int) $php_major_version; $php_minor_version = (int) $php_minor_version; - if ($this->codebase->php_major_version !== $php_major_version - || $this->codebase->php_minor_version !== $php_minor_version - ) { + $analysis_php_version_id = $php_major_version * 10000 + $php_minor_version * 100; + + if ($this->codebase->analysis_php_version_id !== $analysis_php_version_id) { // reset lexer and parser when php version changes StatementsProvider::clearLexer(); StatementsProvider::clearParser(); } - $this->codebase->php_major_version = $php_major_version; - $this->codebase->php_minor_version = $php_minor_version; - $this->codebase->analysis_php_version_id = $php_major_version * 10000 + $php_minor_version * 100; + $this->codebase->analysis_php_version_id = $analysis_php_version_id; $this->codebase->php_version_source = $source; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index bfee36f3ac7..d5df517ce26 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -510,9 +510,7 @@ private static function handleUnpackedArray( if ($unpacked_atomic_type instanceof TKeyedArray) { foreach ($unpacked_atomic_type->properties as $key => $property_value) { if (is_string($key)) { - if ($codebase->php_major_version < 8 || - ($codebase->php_major_version === 8 && $codebase->php_minor_version < 1) - ) { + if ($codebase->analysis_php_version_id <= 80000) { IssueBuffer::maybeAdd( new DuplicateArrayKey( 'String keys are not supported in unpacked arrays', @@ -555,9 +553,7 @@ private static function handleUnpackedArray( $array_creation_info->can_create_objectlike = false; if ($unpacked_atomic_type->type_params[0]->hasString()) { - if ($codebase->php_major_version < 8 || - ($codebase->php_major_version === 8 && $codebase->php_minor_version < 1) - ) { + if ($codebase->analysis_php_version_id <= 80000) { IssueBuffer::maybeAdd( new DuplicateArrayKey( 'String keys are not supported in unpacked arrays', diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 84b777c7e20..4a4ac8c3afb 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -486,7 +486,7 @@ private static function checkFunctionLikeTypeMatches( if ($function_param->is_variadic) { $arg_type = $unpacked_atomic_array->getGenericValueType(); - } elseif ($codebase->php_major_version >= 8 + } elseif ($codebase->analysis_php_version_id >= 80000 && $allow_named_args && isset($unpacked_atomic_array->properties[$function_param->name]) ) { @@ -552,7 +552,7 @@ private static function checkFunctionLikeTypeMatches( continue; } - if (($codebase->php_major_version < 8 || !$allow_named_args) && !$key_type->isInt()) { + if (($codebase->analysis_php_version_id < 80000 || !$allow_named_args) && !$key_type->isInt()) { $invalid_string_key = true; continue; @@ -578,7 +578,7 @@ private static function checkFunctionLikeTypeMatches( 'Method ' . $cased_method_id . ' called with unpacked iterable ' . $arg_type->getId() . ' with invalid key (must be ' - . ($codebase->php_major_version < 8 ? 'int' : 'int|string') . ')', + . ($codebase->analysis_php_version_id < 80000 ? 'int' : 'int|string') . ')', new CodeLocation($statements_analyzer->getSource(), $arg->value), $cased_method_id ), @@ -586,7 +586,7 @@ private static function checkFunctionLikeTypeMatches( ); } if ($invalid_string_key) { - if ($codebase->php_major_version < 8) { + if ($codebase->analysis_php_version_id < 80000) { IssueBuffer::maybeAdd( new $issue_type( 'String keys not supported in unpacked arguments', diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php index 7a61039cdc1..af190ce1d20 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php @@ -125,7 +125,7 @@ public static function fetch( $template_result->lower_bounds[$template_name] = [ 'fn-' . $function_id => [ new TemplateBound( - Type::getInt(false, $codebase->php_major_version) + Type::getInt(false, $codebase->getMajorAnalysisPhpVersion()) ) ] ]; @@ -135,8 +135,7 @@ public static function fetch( new TemplateBound( Type::getInt( false, - 10000 * $codebase->php_major_version - + 100 * $codebase->php_minor_version + $codebase->analysis_php_version_id ) ) ] diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php index cc99d748ec5..44bd32a59b3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php @@ -570,7 +570,7 @@ public static function replaceTemplateTypes( $template_result->lower_bounds[$template_type->param_name] = [ 'fn-' . strtolower((string) $method_id) => [ new TemplateBound( - Type::getInt(false, $codebase->php_major_version) + Type::getInt(false, $codebase->getMajorAnalysisPhpVersion()) ) ] ]; @@ -580,8 +580,7 @@ public static function replaceTemplateTypes( new TemplateBound( Type::getInt( false, - 10000 * $codebase->php_major_version - + 100 * $codebase->php_minor_version + $codebase->analysis_php_version_id ) ) ] diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php index 6951396f72a..97435117100 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php @@ -504,7 +504,7 @@ private static function getMethodReturnType( $template_result->lower_bounds[$template_type->param_name] = [ 'fn-' . strtolower((string)$method_id) => [ new TemplateBound( - Type::getInt(false, $codebase->php_major_version) + Type::getInt(false, $codebase->getMajorAnalysisPhpVersion()) ) ] ]; @@ -514,8 +514,7 @@ private static function getMethodReturnType( new TemplateBound( Type::getInt( false, - 10000 * $codebase->php_major_version - + 100 * $codebase->php_minor_version + $codebase->analysis_php_version_id ) ) ] diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index 4f80b23e0a7..6aee61d6dc9 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -261,7 +261,7 @@ public static function analyze( } if ($stmt instanceof PhpParser\Node\Expr\Cast\Unset_ - && $statements_analyzer->getCodebase()->php_major_version < 8 + && $statements_analyzer->getCodebase()->analysis_php_version_id <= 70400 ) { if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) { return false; diff --git a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php index 0ce2af836e8..bbb020fab50 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php @@ -470,20 +470,20 @@ private static function handleExpression( return YieldFromAnalyzer::analyze($statements_analyzer, $stmt, $context); } - $php_major_version = $statements_analyzer->getCodebase()->php_major_version; - $php_minor_version = $statements_analyzer->getCodebase()->php_minor_version; + $codebase = $statements_analyzer->getCodebase(); + $analysis_php_version_id = $codebase->analysis_php_version_id; - if ($stmt instanceof PhpParser\Node\Expr\Match_ && $php_major_version >= 8) { + if ($stmt instanceof PhpParser\Node\Expr\Match_ && $analysis_php_version_id >= 80000) { return MatchAnalyzer::analyze($statements_analyzer, $stmt, $context); } - if ($stmt instanceof PhpParser\Node\Expr\Throw_ && $php_major_version >= 8) { + if ($stmt instanceof PhpParser\Node\Expr\Throw_ && $analysis_php_version_id >= 80000) { return ThrowAnalyzer::analyze($statements_analyzer, $stmt, $context); } if (($stmt instanceof PhpParser\Node\Expr\NullsafePropertyFetch || $stmt instanceof PhpParser\Node\Expr\NullsafeMethodCall) - && $php_major_version >= 8 + && $analysis_php_version_id >= 80000 ) { return NullsafeAnalyzer::analyze($statements_analyzer, $stmt, $context); } @@ -496,7 +496,7 @@ private static function handleExpression( if (IssueBuffer::accepts( new UnrecognizedExpression( 'Psalm does not understand ' . get_class($stmt) . ' for PHP ' . - $php_major_version . ' ' . $php_minor_version, + $codebase->getMajorAnalysisPhpVersion() . '.' . $codebase->getMinorAnalysisPhpVersion(), new CodeLocation($statements_analyzer->getSource(), $stmt) ), $statements_analyzer->getSuppressedIssues() diff --git a/src/Psalm/Internal/Codebase/ClassLikes.php b/src/Psalm/Internal/Codebase/ClassLikes.php index 372d1da901f..12119c7e8b8 100644 --- a/src/Psalm/Internal/Codebase/ClassLikes.php +++ b/src/Psalm/Internal/Codebase/ClassLikes.php @@ -806,7 +806,10 @@ public function getTraitNode(string $fq_trait_name): PhpParser\Node\Stmt\Trait_ throw new UnexpectedValueException('Storage should exist for ' . $fq_trait_name); } - $file_statements = $this->statements_provider->getStatementsForFile($storage->location->file_path, '7.4'); + $file_statements = $this->statements_provider->getStatementsForFile( + $storage->location->file_path, + ProjectAnalyzer::getInstance()->getCodebase()->analysis_php_version_id + ); $trait_finder = new TraitFinder($fq_trait_name); diff --git a/src/Psalm/Internal/Codebase/InternalCallMapHandler.php b/src/Psalm/Internal/Codebase/InternalCallMapHandler.php index 8af4d0cbd75..a41fa171f0c 100644 --- a/src/Psalm/Internal/Codebase/InternalCallMapHandler.php +++ b/src/Psalm/Internal/Codebase/InternalCallMapHandler.php @@ -347,8 +347,8 @@ public static function getCallablesFromCallMap(string $function_id): ?array public static function getCallMap(): array { $codebase = ProjectAnalyzer::getInstance()->getCodebase(); - $analyzer_major_version = $codebase->php_major_version; - $analyzer_minor_version = $codebase->php_minor_version; + $analyzer_major_version = $codebase->getMajorAnalysisPhpVersion(); + $analyzer_minor_version = $codebase->getMinorAnalysisPhpVersion(); $analyzer_version = $analyzer_major_version . '.' . $analyzer_minor_version; $current_version = self::PHP_MAJOR_VERSION . '.' . self::PHP_MINOR_VERSION; diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php index d2bb03ce945..cc913806c19 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php @@ -422,7 +422,7 @@ public static function parse( $statements = StatementsProvider::parseStatements( $php_string, - $codebase->php_major_version . '.' . $codebase->php_minor_version, + $codebase->analysis_php_version_id, $has_errors ); } catch (Exception $e) { diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php index 476cb781eef..7e573e449c1 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php @@ -1495,8 +1495,7 @@ private function visitPropertyDeclaration( $this->file_storage, $this->storage, $this->aliases, - $this->codebase->php_major_version, - $this->codebase->php_minor_version + $this->codebase->analysis_php_version_id ); $signature_type_location = new CodeLocation( diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php index 3cf9d02950d..f4fec865b1c 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php @@ -340,7 +340,7 @@ public static function enterConditional( ) ) ) { - $php_version_id = $codebase->php_major_version * 10000 + $codebase->php_minor_version * 100; + $php_version_id = $codebase->analysis_php_version_id; $evaluator = new ConstExprEvaluator(function (Expr $expr) use ($php_version_id) { if ($expr instanceof ConstFetch && $expr->name->parts === ['PHP_VERSION_ID']) { return $php_version_id; diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index de938d517b7..01a1aada6fd 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -434,8 +434,7 @@ public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = fal $this->file_storage, $this->classlike_storage, $this->aliases, - $this->codebase->php_major_version, - $this->codebase->php_minor_version + $this->codebase->analysis_php_version_id ); $storage->return_type_location = new CodeLocation( @@ -486,12 +485,14 @@ public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = fal if ($docblock_info) { if ($docblock_info->since_php_major_version && !$this->aliases->namespace) { - if ($docblock_info->since_php_major_version > $this->codebase->php_major_version) { + $analysis_major_php_version = $this->codebase->getMajorAnalysisPhpVersion(); + $analysis_minor_php_version = $this->codebase->getMajorAnalysisPhpVersion(); + if ($docblock_info->since_php_major_version > $analysis_major_php_version) { return false; } - if ($docblock_info->since_php_major_version === $this->codebase->php_major_version - && $docblock_info->since_php_minor_version > $this->codebase->php_minor_version + if ($docblock_info->since_php_major_version === $analysis_major_php_version + && $docblock_info->since_php_minor_version > $analysis_minor_php_version ) { return false; } @@ -828,8 +829,7 @@ private function getTranslatedFunctionParam( $this->file_storage, $this->classlike_storage, $this->aliases, - $this->codebase->php_major_version, - $this->codebase->php_minor_version + $this->codebase->analysis_php_version_id ); if ($is_nullable) { @@ -1043,11 +1043,14 @@ private function createStorageForFunctionLike( } if ($docblock_info) { if ($docblock_info->since_php_major_version && !$this->aliases->namespace) { - if ($docblock_info->since_php_major_version > $this->codebase->php_major_version) { + $analysis_major_php_version = $this->codebase->getMajorAnalysisPhpVersion(); + $analysis_minor_php_version = $this->codebase->getMajorAnalysisPhpVersion(); + if ($docblock_info->since_php_major_version > $analysis_major_php_version) { return false; } - if ($docblock_info->since_php_major_version === $this->codebase->php_major_version - && $docblock_info->since_php_minor_version > $this->codebase->php_minor_version + + if ($docblock_info->since_php_major_version === $analysis_major_php_version + && $docblock_info->since_php_minor_version > $analysis_minor_php_version ) { return false; } @@ -1072,7 +1075,7 @@ private function createStorageForFunctionLike( if ($method_name_lc === strtolower($class_name) && !isset($classlike_storage->methods['__construct']) && strpos($fq_classlike_name, '\\') === false - && $this->codebase->php_major_version < 8 + && $this->codebase->analysis_php_version_id <= 70400 ) { $this->codebase->methods->setDeclaringMethodId( $fq_classlike_name, diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php b/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php index 3dfb0859075..6b992cedd82 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php @@ -27,8 +27,7 @@ public static function resolve( FileStorage $file_storage, ?ClassLikeStorage $classlike_storage, Aliases $aliases, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): Union { if ($hint instanceof PhpParser\Node\UnionType) { $type = null; @@ -44,8 +43,7 @@ public static function resolve( $file_storage, $classlike_storage, $aliases, - $php_major_version, - $php_minor_version + $analysis_php_version_id ); $type = Type::combineUnionTypes($resolved_type, $type); @@ -93,7 +91,7 @@ public static function resolve( $type = Type::parseString( $fq_type_string, - [$php_major_version, $php_minor_version], + $analysis_php_version_id, [] ); diff --git a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php index e99259cfca0..1dccc6dc6f5 100644 --- a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php @@ -237,7 +237,7 @@ public function enterNode(PhpParser\Node $node): ?int $this->functionlike_node_scanners[] = $functionlike_node_scanner; if ($classlike_storage - && $this->codebase->php_major_version >= 8 + && $this->codebase->analysis_php_version_id >= 80000 && $node instanceof PhpParser\Node\Stmt\ClassMethod && strtolower($node->name->name) === '__tostring' ) { diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index 3a5cb57fd80..92ab2887653 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -6,6 +6,7 @@ use PhpParser\ErrorHandler\Collecting; use PhpParser\Node\Stmt; use Psalm\CodeLocation\ParseErrorLocation; +use Psalm\Codebase; use Psalm\Config; use Psalm\Internal\Diff\FileDiffer; use Psalm\Internal\Diff\FileStatementsDiffer; @@ -109,8 +110,11 @@ public function __construct( /** * @return list */ - public function getStatementsForFile(string $file_path, string $php_version, ?Progress $progress = null): array - { + public function getStatementsForFile( + string $file_path, + int $analysis_php_version_id, + ?Progress $progress = null + ): array { unset($this->errors[$file_path]); if ($progress === null) { @@ -133,7 +137,7 @@ public function getStatementsForFile(string $file_path, string $php_version, ?Pr $has_errors = false; - $stmts = self::parseStatements($file_contents, $php_version, $has_errors, $file_path); + $stmts = self::parseStatements($file_contents, $analysis_php_version_id, $has_errors, $file_path); return $stmts ?: []; } @@ -195,7 +199,7 @@ public function getStatementsForFile(string $file_path, string $php_version, ?Pr $stmts = self::parseStatements( $file_contents, - $php_version, + $analysis_php_version_id, $has_errors, $file_path, $existing_file_contents, @@ -416,22 +420,24 @@ public function resetDiffs(): void * @return list */ public static function parseStatements( - string $file_contents, - string $php_version, - bool &$has_errors, + string $file_contents, + int $analysis_php_version_id, + bool &$has_errors, ?string $file_path = null, ?string $existing_file_contents = null, - ?array $existing_statements = null, - ?array $file_changes = null + ?array $existing_statements = null, + ?array $file_changes = null ): array { $attributes = [ 'comments', 'startLine', 'startFilePos', 'endFilePos', ]; if (!self::$lexer) { + $major_version = Codebase::transformPhpVersionId($analysis_php_version_id, 10000); + $minor_version = Codebase::transformPhpVersionId($analysis_php_version_id % 10000, 100); self::$lexer = new PhpParser\Lexer\Emulative([ 'usedAttributes' => $attributes, - 'phpVersion' => $php_version, + 'phpVersion' => $major_version . '.' . $minor_version, ]); } diff --git a/src/Psalm/Internal/Scanner/FileScanner.php b/src/Psalm/Internal/Scanner/FileScanner.php index 1cc9b6fffe9..b77a8794556 100644 --- a/src/Psalm/Internal/Scanner/FileScanner.php +++ b/src/Psalm/Internal/Scanner/FileScanner.php @@ -59,7 +59,7 @@ public function scan( $stmts = $codebase->statements_provider->getStatementsForFile( $file_storage->file_path, - $codebase->php_major_version . '.' . $codebase->php_minor_version, + $codebase->analysis_php_version_id, $progress ); diff --git a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php index 23b1ca16ef0..19c6266bbca 100644 --- a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php @@ -384,7 +384,7 @@ public static function getParserTypeFromPsalmType(Union $type): ?PhpParser\NodeA || $atomic_type instanceof TArray || $atomic_type instanceof TIterable ) { - $identifier_string = $atomic_type->toPhpString(null, [], null, 8, 0); + $identifier_string = $atomic_type->toPhpString(null, [], null, 80000); if ($identifier_string === null) { throw new UnexpectedValueException( diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index 6f35b1ff3a3..b6045f4df91 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -509,7 +509,7 @@ public static function isContainedBy( if ($input_type_part instanceof TNamedObject) { // check whether the object has a __toString method if ($codebase->classOrInterfaceExists($input_type_part->value)) { - if ($codebase->php_major_version >= 8 + if ($codebase->analysis_php_version_id >= 80000 && ($input_type_part->value === 'Stringable' || ($codebase->classlikes->classExists($input_type_part->value) && $codebase->classlikes->classImplements($input_type_part->value, 'Stringable')) diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index 20c9308b2bd..cfd0b12908e 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -105,7 +105,7 @@ class TypeParser */ public static function parseTokens( array $type_tokens, - ?array $php_version = null, + ?int $analysis_php_version_id = null, array $template_type_map = [], array $type_aliases = [] ): Union { @@ -121,9 +121,9 @@ public static function parseTokens( throw new TypeParseTreeException("Invalid type '$only_token[0]'"); } } else { - $only_token[0] = TypeTokenizer::fixScalarTerms($only_token[0], $php_version); + $only_token[0] = TypeTokenizer::fixScalarTerms($only_token[0], $analysis_php_version_id); - $atomic = Atomic::create($only_token[0], $php_version, $template_type_map, $type_aliases); + $atomic = Atomic::create($only_token[0], $analysis_php_version_id, $template_type_map, $type_aliases); $atomic->offset_start = 0; $atomic->offset_end = strlen($only_token[0]); $atomic->text = isset($only_token[2]) && $only_token[2] !== $only_token[0] ? $only_token[2] : null; @@ -137,7 +137,7 @@ public static function parseTokens( $parsed_type = self::getTypeFromTree( $parse_tree, $codebase, - $php_version, + $analysis_php_version_id, $template_type_map, $type_aliases ); @@ -150,18 +150,17 @@ public static function parseTokens( } /** - * @param array{int,int}|null $php_version * @param array> $template_type_map - * @param array $type_aliases + * @param array $type_aliases * * @return Atomic|Union */ public static function getTypeFromTree( ParseTree $parse_tree, - Codebase $codebase, - ?array $php_version = null, - array $template_type_map = [], - array $type_aliases = [] + Codebase $codebase, + ?int $analysis_php_version_id = null, + array $template_type_map = [], + array $type_aliases = [] ): TypeNode { if ($parse_tree instanceof GenericTree) { return self::getTypeFromGenericTree( @@ -378,9 +377,9 @@ public static function getTypeFromTree( throw new TypeParseTreeException('Invalid type \'' . $parse_tree->value . '\''); } - $atomic_type_string = TypeTokenizer::fixScalarTerms($parse_tree->value, $php_version); + $atomic_type_string = TypeTokenizer::fixScalarTerms($parse_tree->value, $analysis_php_version_id); - $atomic_type = Atomic::create($atomic_type_string, $php_version, $template_type_map, $type_aliases); + $atomic_type = Atomic::create($atomic_type_string, $analysis_php_version_id, $template_type_map, $type_aliases); $atomic_type->offset_start = $parse_tree->offset_start; $atomic_type->offset_end = $parse_tree->offset_end; diff --git a/src/Psalm/Internal/Type/TypeTokenizer.php b/src/Psalm/Internal/Type/TypeTokenizer.php index 020a3b270ca..996eb2d41e3 100644 --- a/src/Psalm/Internal/Type/TypeTokenizer.php +++ b/src/Psalm/Internal/Type/TypeTokenizer.php @@ -300,14 +300,11 @@ public static function tokenize(string $string_type, bool $ignore_space = true): } /** - * @param array{int,int}|null $php_version - * - * * @psalm-pure */ public static function fixScalarTerms( string $type_string, - ?array $php_version = null + ?int $analysis_php_version_id = null ): string { $type_string_lc = strtolower($type_string); @@ -330,14 +327,14 @@ public static function fixScalarTerms( switch ($type_string) { case 'boolean': - return $php_version !== null ? $type_string : 'bool'; + return $analysis_php_version_id !== null ? $type_string : 'bool'; case 'integer': - return $php_version !== null ? $type_string : 'int'; + return $analysis_php_version_id !== null ? $type_string : 'int'; case 'double': case 'real': - return $php_version !== null ? $type_string : 'float'; + return $analysis_php_version_id !== null ? $type_string : 'float'; } return $type_string; diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index 6d0c9e0fb45..e71d91d4faf 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -75,14 +75,14 @@ abstract class Type */ public static function parseString( string $type_string, - ?array $php_version = null, + ?int $analysis_php_version_id = null, array $template_type_map = [] ): Union { return TypeParser::parseTokens( TypeTokenizer::tokenize( $type_string ), - $php_version, + $analysis_php_version_id, $template_type_map ); } diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 91378836e53..0385879db43 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -113,15 +113,15 @@ abstract class Atomic implements TypeNode public $text; /** - * @param array{int,int}|null $php_version contains php version when the type comes from signature + * @param int $analysis_php_version_id contains php version when the type comes from signature * @param array> $template_type_map * @param array $type_aliases */ public static function create( string $value, - ?array $php_version = null, - array $template_type_map = [], - array $type_aliases = [] + ?int $analysis_php_version_id = null, + array $template_type_map = [], + array $type_aliases = [] ): Atomic { switch ($value) { case 'int': @@ -137,10 +137,7 @@ public static function create( return new TBool(); case 'void': - if ($php_version === null - || ($php_version[0] > 7) - || ($php_version[0] === 7 && $php_version[1] >= 1) - ) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 70100) { return new TVoid(); } @@ -150,20 +147,14 @@ public static function create( return new TArrayKey(); case 'iterable': - if ($php_version === null - || ($php_version[0] > 7) - || ($php_version[0] === 7 && $php_version[1] >= 1) - ) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 70100) { return new TIterable(); } break; case 'never': - if ($php_version === null - || ($php_version[0] > 8) - || ($php_version[0] === 8 && $php_version[1] >= 1) - ) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 80100) { return new TNever(); } @@ -175,10 +166,7 @@ public static function create( return new TNever(); case 'object': - if ($php_version === null - || ($php_version[0] > 7) - || ($php_version[0] === 7 && $php_version[1] >= 2) - ) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 70200) { return new TObject(); } @@ -221,7 +209,7 @@ public static function create( return new TNonEmptyLowercaseString(); case 'resource': - return $php_version !== null ? new TNamedObject($value) : new TResource(); + return $analysis_php_version_id !== null ? new TNamedObject($value) : new TResource(); case 'resource (closed)': case 'closed-resource': @@ -231,33 +219,33 @@ public static function create( return new TPositiveInt(); case 'numeric': - return $php_version !== null ? new TNamedObject($value) : new TNumeric(); + return $analysis_php_version_id !== null ? new TNamedObject($value) : new TNumeric(); case 'true': - return $php_version !== null ? new TNamedObject($value) : new TTrue(); + return $analysis_php_version_id !== null ? new TNamedObject($value) : new TTrue(); case 'false': - if ($php_version === null || $php_version[0] >= 8) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 80000) { return new TFalse(); } return new TNamedObject($value); case 'empty': - return $php_version !== null ? new TNamedObject($value) : new TEmpty(); + return $analysis_php_version_id !== null ? new TNamedObject($value) : new TEmpty(); case 'scalar': - return $php_version !== null ? new TNamedObject($value) : new TScalar(); + return $analysis_php_version_id !== null ? new TNamedObject($value) : new TScalar(); case 'null': - if ($php_version === null || $php_version[0] >= 8) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 80000) { return new TNull(); } return new TNamedObject($value); case 'mixed': - if ($php_version === null || $php_version[0] >= 8) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 80000) { return new TMixed(); } @@ -625,11 +613,10 @@ abstract public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string; - abstract public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool; + abstract public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool; public function replaceTemplateTypesWithStandins( TemplateResult $template_result, diff --git a/src/Psalm/Type/Atomic/CallableTrait.php b/src/Psalm/Type/Atomic/CallableTrait.php index cd0c026d0c1..062cd647187 100644 --- a/src/Psalm/Type/Atomic/CallableTrait.php +++ b/src/Psalm/Type/Atomic/CallableTrait.php @@ -141,8 +141,7 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): string { if ($this instanceof TNamedObject) { return parent::toNamespacedString($namespace, $aliased_classes, $this_class, true); diff --git a/src/Psalm/Type/Atomic/Scalar.php b/src/Psalm/Type/Atomic/Scalar.php index f37518cd289..dbc2f1a4691 100644 --- a/src/Psalm/Type/Atomic/Scalar.php +++ b/src/Psalm/Type/Atomic/Scalar.php @@ -6,7 +6,7 @@ abstract class Scalar extends Atomic { - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return true; } diff --git a/src/Psalm/Type/Atomic/TAnonymousClassInstance.php b/src/Psalm/Type/Atomic/TAnonymousClassInstance.php index 1d8583e41f8..57cda6dc01d 100644 --- a/src/Psalm/Type/Atomic/TAnonymousClassInstance.php +++ b/src/Psalm/Type/Atomic/TAnonymousClassInstance.php @@ -26,12 +26,9 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version > 7 - || ($php_major_version === 7 && $php_minor_version >= 2) - ? ($this->extends ?? 'object') : null; + return $analysis_php_version_id >= 70200 ? ($this->extends ?? 'object') : null; } /** diff --git a/src/Psalm/Type/Atomic/TArray.php b/src/Psalm/Type/Atomic/TArray.php index 05e31a4762f..eabbe75e72b 100644 --- a/src/Psalm/Type/Atomic/TArray.php +++ b/src/Psalm/Type/Atomic/TArray.php @@ -47,13 +47,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): string { return $this->getKey(); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return $this->type_params[0]->isArrayKey() && $this->type_params[1]->isMixed(); } diff --git a/src/Psalm/Type/Atomic/TArrayKey.php b/src/Psalm/Type/Atomic/TArrayKey.php index 038b85cfc73..7c53bd1dc6c 100644 --- a/src/Psalm/Type/Atomic/TArrayKey.php +++ b/src/Psalm/Type/Atomic/TArrayKey.php @@ -24,13 +24,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TAssertionFalsy.php b/src/Psalm/Type/Atomic/TAssertionFalsy.php index db43e12bf75..01ca6cc429c 100644 --- a/src/Psalm/Type/Atomic/TAssertionFalsy.php +++ b/src/Psalm/Type/Atomic/TAssertionFalsy.php @@ -31,13 +31,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TBool.php b/src/Psalm/Type/Atomic/TBool.php index adae27f9d58..bdd48fb463d 100644 --- a/src/Psalm/Type/Atomic/TBool.php +++ b/src/Psalm/Type/Atomic/TBool.php @@ -24,9 +24,8 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version >= 7 ? 'bool' : null; + return $analysis_php_version_id >= 70000 ? 'bool' : null; } } diff --git a/src/Psalm/Type/Atomic/TCallable.php b/src/Psalm/Type/Atomic/TCallable.php index fb92d7e6786..217c717fe00 100644 --- a/src/Psalm/Type/Atomic/TCallable.php +++ b/src/Psalm/Type/Atomic/TCallable.php @@ -23,13 +23,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): string { return 'callable'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return $this->params === null && $this->return_type === null; } diff --git a/src/Psalm/Type/Atomic/TCallableObject.php b/src/Psalm/Type/Atomic/TCallableObject.php index 3ba0a8a7b56..c210976beec 100644 --- a/src/Psalm/Type/Atomic/TCallableObject.php +++ b/src/Psalm/Type/Atomic/TCallableObject.php @@ -24,15 +24,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version > 7 - || ($php_major_version === 7 && $php_minor_version >= 2) - ? 'object' : null; + return $analysis_php_version_id >= 72000 ? 'object' : null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TCallableString.php b/src/Psalm/Type/Atomic/TCallableString.php index 803120df672..c4cdeb42662 100644 --- a/src/Psalm/Type/Atomic/TCallableString.php +++ b/src/Psalm/Type/Atomic/TCallableString.php @@ -18,7 +18,7 @@ public function getId(bool $nested = false): string return $this->getKey(); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TClassConstant.php b/src/Psalm/Type/Atomic/TClassConstant.php index 7790806d32a..2b71c1ae33d 100644 --- a/src/Psalm/Type/Atomic/TClassConstant.php +++ b/src/Psalm/Type/Atomic/TClassConstant.php @@ -49,13 +49,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TClassString.php b/src/Psalm/Type/Atomic/TClassString.php index 745778c7369..33658d0e203 100644 --- a/src/Psalm/Type/Atomic/TClassString.php +++ b/src/Psalm/Type/Atomic/TClassString.php @@ -66,8 +66,7 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return 'string'; } @@ -104,7 +103,7 @@ public function toNamespacedString( return 'class-string<\\' . $this->as . '>'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TClassStringMap.php b/src/Psalm/Type/Atomic/TClassStringMap.php index edef7eec388..6c13a7acd6a 100644 --- a/src/Psalm/Type/Atomic/TClassStringMap.php +++ b/src/Psalm/Type/Atomic/TClassStringMap.php @@ -126,13 +126,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): string { return 'array'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TClosedResource.php b/src/Psalm/Type/Atomic/TClosedResource.php index c8391b4c532..8ebbbfaeffd 100644 --- a/src/Psalm/Type/Atomic/TClosedResource.php +++ b/src/Psalm/Type/Atomic/TClosedResource.php @@ -31,13 +31,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TClosure.php b/src/Psalm/Type/Atomic/TClosure.php index 5e3a9dfe08f..7703aa82cb7 100644 --- a/src/Psalm/Type/Atomic/TClosure.php +++ b/src/Psalm/Type/Atomic/TClosure.php @@ -12,7 +12,7 @@ class TClosure extends TNamedObject /** @var array */ public $byref_uses = []; - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TConditional.php b/src/Psalm/Type/Atomic/TConditional.php index 2c12fea6078..496e1b3d492 100644 --- a/src/Psalm/Type/Atomic/TConditional.php +++ b/src/Psalm/Type/Atomic/TConditional.php @@ -106,8 +106,7 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } @@ -130,7 +129,7 @@ public function getChildNodes(): array return [$this->conditional_type, $this->if_type, $this->else_type]; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TDependentGetClass.php b/src/Psalm/Type/Atomic/TDependentGetClass.php index d53e53b2b6c..a22e8476132 100644 --- a/src/Psalm/Type/Atomic/TDependentGetClass.php +++ b/src/Psalm/Type/Atomic/TDependentGetClass.php @@ -56,7 +56,7 @@ public function getReplacement(): Atomic return new TClassString(); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TDependentGetDebugType.php b/src/Psalm/Type/Atomic/TDependentGetDebugType.php index 1ca555270f5..1bdf96486eb 100644 --- a/src/Psalm/Type/Atomic/TDependentGetDebugType.php +++ b/src/Psalm/Type/Atomic/TDependentGetDebugType.php @@ -39,7 +39,7 @@ public function getReplacement(): Atomic return new TString(); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TDependentGetType.php b/src/Psalm/Type/Atomic/TDependentGetType.php index 1c06fcda5df..4b91601c33b 100644 --- a/src/Psalm/Type/Atomic/TDependentGetType.php +++ b/src/Psalm/Type/Atomic/TDependentGetType.php @@ -22,7 +22,7 @@ public function __construct(string $typeof) $this->typeof = $typeof; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TDependentListKey.php b/src/Psalm/Type/Atomic/TDependentListKey.php index c5bbd991e76..74f59173330 100644 --- a/src/Psalm/Type/Atomic/TDependentListKey.php +++ b/src/Psalm/Type/Atomic/TDependentListKey.php @@ -44,7 +44,7 @@ public function getReplacement(): Atomic return new TInt(); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TEmpty.php b/src/Psalm/Type/Atomic/TEmpty.php index 2cfa456f779..d0fd4a78537 100644 --- a/src/Psalm/Type/Atomic/TEmpty.php +++ b/src/Psalm/Type/Atomic/TEmpty.php @@ -26,8 +26,7 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } diff --git a/src/Psalm/Type/Atomic/TEnumCase.php b/src/Psalm/Type/Atomic/TEnumCase.php index a6242306f8d..de6f0a52fe9 100644 --- a/src/Psalm/Type/Atomic/TEnumCase.php +++ b/src/Psalm/Type/Atomic/TEnumCase.php @@ -33,13 +33,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return $this->value; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TFalse.php b/src/Psalm/Type/Atomic/TFalse.php index 5ffd3112f39..c52fa09bed0 100644 --- a/src/Psalm/Type/Atomic/TFalse.php +++ b/src/Psalm/Type/Atomic/TFalse.php @@ -17,7 +17,7 @@ public function getKey(bool $include_extra = true): string return 'false'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TFloat.php b/src/Psalm/Type/Atomic/TFloat.php index 76a7666a2ff..d5ac7ea901b 100644 --- a/src/Psalm/Type/Atomic/TFloat.php +++ b/src/Psalm/Type/Atomic/TFloat.php @@ -24,9 +24,8 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version >= 7 ? 'float' : null; + return $analysis_php_version_id >= 70000 ? 'float' : null; } } diff --git a/src/Psalm/Type/Atomic/TGenericObject.php b/src/Psalm/Type/Atomic/TGenericObject.php index e0f77432e19..79bff4589a0 100644 --- a/src/Psalm/Type/Atomic/TGenericObject.php +++ b/src/Psalm/Type/Atomic/TGenericObject.php @@ -57,7 +57,7 @@ public function getKey(bool $include_extra = true): string return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } @@ -69,16 +69,11 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { $result = $this->toNamespacedString($namespace, $aliased_classes, $this_class, true); $intersection = strrpos($result, '&'); - if ($intersection === false || ( - ($php_major_version === 8 && $php_minor_version >= 1) || - ($php_major_version >= 9) - ) - ) { + if ($intersection === false || $analysis_php_version_id >= 80100) { return $result; } return substr($result, $intersection+1); diff --git a/src/Psalm/Type/Atomic/THtmlEscapedString.php b/src/Psalm/Type/Atomic/THtmlEscapedString.php index b6a77f56e38..99d1056fc23 100644 --- a/src/Psalm/Type/Atomic/THtmlEscapedString.php +++ b/src/Psalm/Type/Atomic/THtmlEscapedString.php @@ -17,7 +17,7 @@ public function getId(bool $nested = false): string return $this->getKey(); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TInt.php b/src/Psalm/Type/Atomic/TInt.php index 27191b81866..c40c6d23244 100644 --- a/src/Psalm/Type/Atomic/TInt.php +++ b/src/Psalm/Type/Atomic/TInt.php @@ -24,9 +24,8 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version >= 7 ? 'int' : null; + return $analysis_php_version_id >= 70000 ? 'int' : null; } } diff --git a/src/Psalm/Type/Atomic/TIntMask.php b/src/Psalm/Type/Atomic/TIntMask.php index cf6cd4c61b9..86a4b5fccfe 100644 --- a/src/Psalm/Type/Atomic/TIntMask.php +++ b/src/Psalm/Type/Atomic/TIntMask.php @@ -64,7 +64,7 @@ public function toNamespacedString( return 'int-mask<' . substr($s, 0, -2) . '>'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TIntMaskOf.php b/src/Psalm/Type/Atomic/TIntMaskOf.php index 70e5645239e..512dd42bc3b 100644 --- a/src/Psalm/Type/Atomic/TIntMaskOf.php +++ b/src/Psalm/Type/Atomic/TIntMaskOf.php @@ -50,7 +50,7 @@ public function toNamespacedString( . '>'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TIntRange.php b/src/Psalm/Type/Atomic/TIntRange.php index 5571823f271..dbdbadb9db1 100644 --- a/src/Psalm/Type/Atomic/TIntRange.php +++ b/src/Psalm/Type/Atomic/TIntRange.php @@ -38,7 +38,7 @@ public function getKey(bool $include_extra = true): string return 'int<' . ($this->min_bound ?? 'min') . ', ' . ($this->max_bound ?? 'max') . '>'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TIterable.php b/src/Psalm/Type/Atomic/TIterable.php index b7aea22cf56..010dcd2aac7 100644 --- a/src/Psalm/Type/Atomic/TIterable.php +++ b/src/Psalm/Type/Atomic/TIterable.php @@ -89,16 +89,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version > 7 - || ($php_major_version === 7 && $php_minor_version >= 1) - ? 'iterable' - : null; + return $analysis_php_version_id >= 70100 ? 'iterable' : null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return $this->type_params[0]->isMixed() && $this->type_params[1]->isMixed(); } diff --git a/src/Psalm/Type/Atomic/TKeyOfClassConstant.php b/src/Psalm/Type/Atomic/TKeyOfClassConstant.php index 05d2e7101a0..189cda0df2a 100644 --- a/src/Psalm/Type/Atomic/TKeyOfClassConstant.php +++ b/src/Psalm/Type/Atomic/TKeyOfClassConstant.php @@ -47,13 +47,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TKeyedArray.php b/src/Psalm/Type/Atomic/TKeyedArray.php index aea960c8346..002e63548f6 100644 --- a/src/Psalm/Type/Atomic/TKeyedArray.php +++ b/src/Psalm/Type/Atomic/TKeyedArray.php @@ -213,13 +213,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): string { return $this->getKey(); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TList.php b/src/Psalm/Type/Atomic/TList.php index 3d5136530d4..75919c8b44d 100644 --- a/src/Psalm/Type/Atomic/TList.php +++ b/src/Psalm/Type/Atomic/TList.php @@ -97,13 +97,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): string { return 'array'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TLiteralClassString.php b/src/Psalm/Type/Atomic/TLiteralClassString.php index a36429d3cf7..5cdb2125109 100644 --- a/src/Psalm/Type/Atomic/TLiteralClassString.php +++ b/src/Psalm/Type/Atomic/TLiteralClassString.php @@ -42,13 +42,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): string { return 'string'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TLowercaseString.php b/src/Psalm/Type/Atomic/TLowercaseString.php index b1b34cd4a43..5fc884fe570 100644 --- a/src/Psalm/Type/Atomic/TLowercaseString.php +++ b/src/Psalm/Type/Atomic/TLowercaseString.php @@ -9,7 +9,7 @@ public function getId(bool $nested = false): string return 'lowercase-string'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TMixed.php b/src/Psalm/Type/Atomic/TMixed.php index 00aaa08ade7..bbb21545d2f 100644 --- a/src/Psalm/Type/Atomic/TMixed.php +++ b/src/Psalm/Type/Atomic/TMixed.php @@ -34,15 +34,14 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version >= 8 ? 'mixed' : null; + return $analysis_php_version_id >= 80000 ? 'mixed' : null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { - return $php_major_version >= 8; + return $analysis_php_version_id >= 80000; } public function getAssertionString(bool $exact = false): string diff --git a/src/Psalm/Type/Atomic/TNamedObject.php b/src/Psalm/Type/Atomic/TNamedObject.php index e9eebc94656..1e5113fbe29 100644 --- a/src/Psalm/Type/Atomic/TNamedObject.php +++ b/src/Psalm/Type/Atomic/TNamedObject.php @@ -118,32 +118,27 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { if ($this->value === 'static') { - return $php_major_version >= 8 ? 'static' : null; + return $analysis_php_version_id >= 80000 ? 'static' : null; } if ($this->was_static && $this->value === $this_class) { - return $php_major_version >= 8 ? 'static' : 'self'; + return $analysis_php_version_id >= 80000 ? 'static' : 'self'; } $result = $this->toNamespacedString($namespace, $aliased_classes, $this_class, false); $intersection = strrpos($result, '&'); - if ($intersection === false || ( - ($php_major_version === 8 && $php_minor_version >= 1) || - ($php_major_version >= 9) - ) - ) { + if ($intersection === false || $analysis_php_version_id >= 80100) { return $result; } return substr($result, $intersection+1); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { - return ($this->value !== 'static' && $this->was_static === false) || $php_major_version >= 8; + return ($this->value !== 'static' && $this->was_static === false) || $analysis_php_version_id >= 80000; } public function replaceTemplateTypesWithArgTypes( diff --git a/src/Psalm/Type/Atomic/TNever.php b/src/Psalm/Type/Atomic/TNever.php index 1b972d0b3ea..5e624c0ab49 100644 --- a/src/Psalm/Type/Atomic/TNever.php +++ b/src/Psalm/Type/Atomic/TNever.php @@ -27,13 +27,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TNonEmptyLowercaseString.php b/src/Psalm/Type/Atomic/TNonEmptyLowercaseString.php index 1d331e8d138..9769e7f605b 100644 --- a/src/Psalm/Type/Atomic/TNonEmptyLowercaseString.php +++ b/src/Psalm/Type/Atomic/TNonEmptyLowercaseString.php @@ -15,7 +15,7 @@ public function getId(bool $nested = false): string /** * @return false */ - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TNonspecificLiteralInt.php b/src/Psalm/Type/Atomic/TNonspecificLiteralInt.php index 8a04903969a..ac920d5a68c 100644 --- a/src/Psalm/Type/Atomic/TNonspecificLiteralInt.php +++ b/src/Psalm/Type/Atomic/TNonspecificLiteralInt.php @@ -13,7 +13,7 @@ public function __toString(): string return 'literal-int'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TNonspecificLiteralString.php b/src/Psalm/Type/Atomic/TNonspecificLiteralString.php index 30d6ac97c1d..fe4c46b57f0 100644 --- a/src/Psalm/Type/Atomic/TNonspecificLiteralString.php +++ b/src/Psalm/Type/Atomic/TNonspecificLiteralString.php @@ -13,7 +13,7 @@ public function __toString(): string return 'literal-string'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TNull.php b/src/Psalm/Type/Atomic/TNull.php index d95759e63d9..2efc36d2417 100644 --- a/src/Psalm/Type/Atomic/TNull.php +++ b/src/Psalm/Type/Atomic/TNull.php @@ -26,13 +26,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TNumeric.php b/src/Psalm/Type/Atomic/TNumeric.php index 8651ff1c998..6b988210a6d 100644 --- a/src/Psalm/Type/Atomic/TNumeric.php +++ b/src/Psalm/Type/Atomic/TNumeric.php @@ -24,13 +24,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TNumericString.php b/src/Psalm/Type/Atomic/TNumericString.php index c86027a442f..001eed685d0 100644 --- a/src/Psalm/Type/Atomic/TNumericString.php +++ b/src/Psalm/Type/Atomic/TNumericString.php @@ -22,7 +22,7 @@ public function getId(bool $nested = false): string return $this->getKey(); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TObject.php b/src/Psalm/Type/Atomic/TObject.php index e2a87219457..c7cc67fff5b 100644 --- a/src/Psalm/Type/Atomic/TObject.php +++ b/src/Psalm/Type/Atomic/TObject.php @@ -26,16 +26,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version > 7 - || ($php_major_version === 7 && $php_minor_version >= 2) - ? $this->getKey() - : null; + return $analysis_php_version_id >= 70200 ? $this->getKey() : null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return true; } diff --git a/src/Psalm/Type/Atomic/TObjectWithProperties.php b/src/Psalm/Type/Atomic/TObjectWithProperties.php index fa68b797275..34f799b3f68 100644 --- a/src/Psalm/Type/Atomic/TObjectWithProperties.php +++ b/src/Psalm/Type/Atomic/TObjectWithProperties.php @@ -175,13 +175,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): string { return $this->getKey(); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TPositiveInt.php b/src/Psalm/Type/Atomic/TPositiveInt.php index eb345a0ba1a..b8335df859a 100644 --- a/src/Psalm/Type/Atomic/TPositiveInt.php +++ b/src/Psalm/Type/Atomic/TPositiveInt.php @@ -20,7 +20,7 @@ public function __toString(): string /** * @return false */ - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TResource.php b/src/Psalm/Type/Atomic/TResource.php index 49ee650902b..47bde1d24aa 100644 --- a/src/Psalm/Type/Atomic/TResource.php +++ b/src/Psalm/Type/Atomic/TResource.php @@ -26,13 +26,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TScalar.php b/src/Psalm/Type/Atomic/TScalar.php index 0cb6f0b80c1..5954a75e7f0 100644 --- a/src/Psalm/Type/Atomic/TScalar.php +++ b/src/Psalm/Type/Atomic/TScalar.php @@ -25,13 +25,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TString.php b/src/Psalm/Type/Atomic/TString.php index 7312b16f43f..986d585b85a 100644 --- a/src/Psalm/Type/Atomic/TString.php +++ b/src/Psalm/Type/Atomic/TString.php @@ -14,10 +14,9 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version >= 7 ? 'string' : null; + return $analysis_php_version_id >= 70000 ? 'string' : null; } public function __toString(): string diff --git a/src/Psalm/Type/Atomic/TTemplateIndexedAccess.php b/src/Psalm/Type/Atomic/TTemplateIndexedAccess.php index 72e72c38f7c..69de28a65d6 100644 --- a/src/Psalm/Type/Atomic/TTemplateIndexedAccess.php +++ b/src/Psalm/Type/Atomic/TTemplateIndexedAccess.php @@ -53,13 +53,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TTemplateParam.php b/src/Psalm/Type/Atomic/TTemplateParam.php index 66c0af836c5..517955bd3f3 100644 --- a/src/Psalm/Type/Atomic/TTemplateParam.php +++ b/src/Psalm/Type/Atomic/TTemplateParam.php @@ -81,8 +81,7 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } @@ -121,7 +120,7 @@ public function getChildNodes(): array return [$this->as]; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TTraitString.php b/src/Psalm/Type/Atomic/TTraitString.php index 65008a4d061..d06a057ec34 100644 --- a/src/Psalm/Type/Atomic/TTraitString.php +++ b/src/Psalm/Type/Atomic/TTraitString.php @@ -29,8 +29,7 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return 'string'; } @@ -48,7 +47,7 @@ public function toNamespacedString( return 'trait-string'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TTrue.php b/src/Psalm/Type/Atomic/TTrue.php index 29313a0d457..637e52ba24b 100644 --- a/src/Psalm/Type/Atomic/TTrue.php +++ b/src/Psalm/Type/Atomic/TTrue.php @@ -17,7 +17,7 @@ public function getKey(bool $include_extra = true): string return 'true'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TTypeAlias.php b/src/Psalm/Type/Atomic/TTypeAlias.php index 788669128b7..9341e2e4a97 100644 --- a/src/Psalm/Type/Atomic/TTypeAlias.php +++ b/src/Psalm/Type/Atomic/TTypeAlias.php @@ -70,13 +70,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TValueOfClassConstant.php b/src/Psalm/Type/Atomic/TValueOfClassConstant.php index cbeb6b4c241..6821650bae1 100644 --- a/src/Psalm/Type/Atomic/TValueOfClassConstant.php +++ b/src/Psalm/Type/Atomic/TValueOfClassConstant.php @@ -44,13 +44,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/src/Psalm/Type/Atomic/TVoid.php b/src/Psalm/Type/Atomic/TVoid.php index cec58cd4e99..4241c5e0818 100644 --- a/src/Psalm/Type/Atomic/TVoid.php +++ b/src/Psalm/Type/Atomic/TVoid.php @@ -26,15 +26,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { - return $php_major_version > 7 - || ($php_major_version === 7 && $php_minor_version >= 1) - ? $this->getKey() : null; + return $analysis_php_version_id >= 70100 ? $this->getKey() : null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return true; } diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index d43fb967936..5972e03a12a 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -511,17 +511,16 @@ public function toNamespacedString( */ public function toPhpString( ?string $namespace, - array $aliased_classes, + array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { if (!$this->isSingleAndMaybeNullable()) { - if ($php_major_version < 8) { + if ($analysis_php_version_id < 80000) { return null; } - } elseif ($php_major_version < 7 - || (isset($this->types['null']) && $php_major_version === 7 && $php_minor_version < 1) + } elseif ($analysis_php_version_id < 70000 + || (isset($this->types['null']) && $analysis_php_version_id < 70100) ) { return null; } @@ -551,8 +550,7 @@ public function toPhpString( $namespace, $aliased_classes, $this_class, - $php_major_version, - $php_minor_version + $analysis_php_version_id ); if (!$php_type) { @@ -571,7 +569,7 @@ public function toPhpString( return implode('|', array_unique($php_types)); } - if ($php_major_version < 8) { + if ($analysis_php_version_id < 80000) { return ($nullable ? '?' : '') . implode('|', array_unique($php_types)); } if ($nullable) { @@ -580,9 +578,9 @@ public function toPhpString( return implode('|', array_unique($php_types)); } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { - if (!$this->isSingleAndMaybeNullable() && $php_major_version < 8) { + if (!$this->isSingleAndMaybeNullable() && $analysis_php_version_id < 80000) { return false; } @@ -598,8 +596,8 @@ public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_ return !array_filter( $types, - function ($atomic_type) use ($php_major_version, $php_minor_version) { - return !$atomic_type->canBeFullyExpressedInPhp($php_major_version, $php_minor_version); + function ($atomic_type) use ($analysis_php_version_id) { + return !$atomic_type->canBeFullyExpressedInPhp($analysis_php_version_id); } ); } diff --git a/tests/AlgebraTest.php b/tests/AlgebraTest.php index ac5c42f0f79..406e66816d6 100644 --- a/tests/AlgebraTest.php +++ b/tests/AlgebraTest.php @@ -85,7 +85,7 @@ public function testCombinatorialExpansion(): void $has_errors = false; - $dnf_stmt = StatementsProvider::parseStatements($dnf, '7.4', $has_errors)[0]; + $dnf_stmt = StatementsProvider::parseStatements($dnf, 70400, $has_errors)[0]; $this->assertInstanceOf(PhpParser\Node\Stmt\Expression::class, $dnf_stmt); diff --git a/tests/Config/Plugin/Hook/StringProvider/TSqlSelectString.php b/tests/Config/Plugin/Hook/StringProvider/TSqlSelectString.php index 8540f3bd097..4be059215ef 100644 --- a/tests/Config/Plugin/Hook/StringProvider/TSqlSelectString.php +++ b/tests/Config/Plugin/Hook/StringProvider/TSqlSelectString.php @@ -19,7 +19,7 @@ public function getId(bool $nested = true): string return 'sql-select-string(' . $this->value . ')'; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } diff --git a/tests/FileDiffTest.php b/tests/FileDiffTest.php index 86b8e0ee105..f634abbc530 100644 --- a/tests/FileDiffTest.php +++ b/tests/FileDiffTest.php @@ -37,8 +37,8 @@ public function testCode( $has_errors = false; - $a_stmts = StatementsProvider::parseStatements($a, '7.4', $has_errors); - $b_stmts = StatementsProvider::parseStatements($b, '7.4', $has_errors); + $a_stmts = StatementsProvider::parseStatements($a, 70400, $has_errors); + $b_stmts = StatementsProvider::parseStatements($b, 70400, $has_errors); $diff = FileStatementsDiffer::diff($a_stmts, $b_stmts, $a, $b); @@ -101,7 +101,7 @@ public function testPartialAstDiff( $has_errors = false; - $a_stmts = StatementsProvider::parseStatements($a, '7.4', $has_errors); + $a_stmts = StatementsProvider::parseStatements($a, 70400, $has_errors); $traverser = new PhpParser\NodeTraverser; $traverser->addVisitor(new CloningVisitor); @@ -111,8 +111,8 @@ public function testPartialAstDiff( $this->assertTreesEqual($a_stmts, $a_stmts_copy); - $b_stmts = StatementsProvider::parseStatements($b, '7.4', $has_errors, null, $a, $a_stmts_copy, $file_changes); - $b_clean_stmts = StatementsProvider::parseStatements($b, '7.4', $has_errors); + $b_stmts = StatementsProvider::parseStatements($b, 70400, $has_errors, null, $a, $a_stmts_copy, $file_changes); + $b_clean_stmts = StatementsProvider::parseStatements($b, 70400, $has_errors); $this->assertTreesEqual($b_clean_stmts, $b_stmts); From b41722ef74b65159e4ba3ec9a2ff18d38e0a1cde Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 2 Jan 2022 13:41:15 +0200 Subject: [PATCH 04/78] Added 'release:removed' as a valid PR label --- .github/workflows/pr-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml index 1366a7ef1a9..30761023d08 100644 --- a/.github/workflows/pr-labels.yml +++ b/.github/workflows/pr-labels.yml @@ -10,4 +10,4 @@ jobs: with: mode: minimum count: 1 - labels: "release:typo, release:fix, release:feature, release:deprecation, release:internal, release:docs" + labels: "release:typo, release:fix, release:feature, release:deprecation, release:internal, release:docs, release:removed" From d3729c06adc1a8249e69c14c06398285a85df0f7 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 2 Jan 2022 14:39:44 +0200 Subject: [PATCH 05/78] Document BC breaks --- UPGRADING.md | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 UPGRADING.md diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 00000000000..d648e943f68 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,123 @@ +# Upgrading from Psalm 4 to Psalm 5 +## Changed + - [BC] The parameter `$php_version` of `Psalm\Type\Atomic::create()` renamed + to `$analysis_php_version_id` and changed from `array|null` to `int|null`. + Previously it accepted PHP version as `array{major_version, minor_version}` + while now it accepts version ID, similar to how [`PHP_VERSION_ID` is + calculated](https://www.php.net/manual/en/reserved.constants.php#constant.php-version-id). + + - [BC] The parameter `$php_version` of `Psalm\Type::parseString()` renamed to + `$analysis_php_version_id` and changed from `array|null` to `int|null`. + Previously it accepted PHP version as `array{major_version, minor_version}` + while now it accepts version ID. + + - [BC] Parameter 0 of `canBeFullyExpressedInPhp()` of the classes listed below + changed name from `php_major_version` to `analysis_php_version_id`. + Previously it accepted major PHP version as int (e.g. `7`), while now it + accepts version ID. Classes affected: + - `Psalm\Type\Atomic` + - `Psalm\Type\Atomic\Scalar` + - `Psalm\Type\Atomic\TArray` + - `Psalm\Type\Atomic\TArrayKey` + - `Psalm\Type\Atomic\TAssertionFalsy` + - `Psalm\Type\Atomic\TCallable` + - `Psalm\Type\Atomic\TCallableObject` + - `Psalm\Type\Atomic\TCallableString` + - `Psalm\Type\Atomic\TClassConstant` + - `Psalm\Type\Atomic\TClassString` + - `Psalm\Type\Atomic\TClassStringMap` + - `Psalm\Type\Atomic\TClosedResource` + - `Psalm\Type\Atomic\TClosure` + - `Psalm\Type\Atomic\TConditional` + - `Psalm\Type\Atomic\TDependentGetClass` + - `Psalm\Type\Atomic\TDependentGetDebugType` + - `Psalm\Type\Atomic\TDependentGetType` + - `Psalm\Type\Atomic\TDependentListKey` + - `Psalm\Type\Atomic\TEnumCase` + - `Psalm\Type\Atomic\TFalse` + - `Psalm\Type\Atomic\TGenericObject` + - `Psalm\Type\Atomic\THtmlEscapedString` + - `Psalm\Type\Atomic\TIntMask` + - `Psalm\Type\Atomic\TIntMaskOf` + - `Psalm\Type\Atomic\TIntRange` + - `Psalm\Type\Atomic\TIterable` + - `Psalm\Type\Atomic\TKeyedArray` + - `Psalm\Type\Atomic\TKeyOfClassConstant` + - `Psalm\Type\Atomic\TList` + - `Psalm\Type\Atomic\TLiteralClassString` + - `Psalm\Type\Atomic\TLowercaseString` + - `Psalm\Type\Atomic\TMixed` + - `Psalm\Type\Atomic\TNamedObject` + - `Psalm\Type\Atomic\TNever` + - `Psalm\Type\Atomic\TNonEmptyLowercaseString` + - `Psalm\Type\Atomic\TNonspecificLiteralInt` + - `Psalm\Type\Atomic\TNonspecificLiteralString` + - `Psalm\Type\Atomic\TNull` + - `Psalm\Type\Atomic\TNumeric` + - `Psalm\Type\Atomic\TNumericString` + - `Psalm\Type\Atomic\TObject` + - `Psalm\Type\Atomic\TObjectWithProperties` + - `Psalm\Type\Atomic\TPositiveInt` + - `Psalm\Type\Atomic\TResource` + - `Psalm\Type\Atomic\TScalar` + - `Psalm\Type\Atomic\TTemplateIndexedAccess` + - `Psalm\Type\Atomic\TTemplateParam` + - `Psalm\Type\Atomic\TTraitString` + - `Psalm\Type\Atomic\TTrue` + - `Psalm\Type\Atomic\TTypeAlias` + - `Psalm\Type\Atomic\TValueOfClassConstant` + - `Psalm\Type\Atomic\TVoid` + - `Psalm\Type\Union` + + - [BC] Parameter 3 of `toPhpString()` of methods listed below changed name + from `php_major_version` to `analysis_php_version_id`. Previously it + accepted major PHP version as int (e.g. `7`), while now it accepts version + ID. Classes affected: + - `Psalm\Type\Atomic` + - `Psalm\Type\Atomic\CallableTrait` + - `Psalm\Type\Atomic\TAnonymousClassInstance` + - `Psalm\Type\Atomic\TArray` + - `Psalm\Type\Atomic\TArrayKey` + - `Psalm\Type\Atomic\TAssertionFalsy` + - `Psalm\Type\Atomic\TBool` + - `Psalm\Type\Atomic\TCallable` + - `Psalm\Type\Atomic\TCallableObject` + - `Psalm\Type\Atomic\TClassConstant` + - `Psalm\Type\Atomic\TClassString` + - `Psalm\Type\Atomic\TClassStringMap` + - `Psalm\Type\Atomic\TClosedResource` + - `Psalm\Type\Atomic\TConditional` + - `Psalm\Type\Atomic\TEmpty` + - `Psalm\Type\Atomic\TEnumCase` + - `Psalm\Type\Atomic\TFloat` + - `Psalm\Type\Atomic\TGenericObject` + - `Psalm\Type\Atomic\TInt` + - `Psalm\Type\Atomic\TIterable` + - `Psalm\Type\Atomic\TKeyedArray` + - `Psalm\Type\Atomic\TKeyOfClassConstant` + - `Psalm\Type\Atomic\TList` + - `Psalm\Type\Atomic\TLiteralClassString` + - `Psalm\Type\Atomic\TMixed` + - `Psalm\Type\Atomic\TNamedObject` + - `Psalm\Type\Atomic\TNever` + - `Psalm\Type\Atomic\TNull` + - `Psalm\Type\Atomic\TNumeric` + - `Psalm\Type\Atomic\TObject` + - `Psalm\Type\Atomic\TObjectWithProperties` + - `Psalm\Type\Atomic\TResource` + - `Psalm\Type\Atomic\TScalar` + - `Psalm\Type\Atomic\TString` + - `Psalm\Type\Atomic\TTemplateIndexedAccess` + - `Psalm\Type\Atomic\TTemplateParam` + - `Psalm\Type\Atomic\TTraitString` + - `Psalm\Type\Atomic\TTypeAlias` + - `Psalm\Type\Atomic\TValueOfClassConstant` + - `Psalm\Type\Atomic\TVoid` + - `Psalm\Type\Union` + +## Removed + - [BC] Property `Psalm\Codebase::$php_major_version` was removed, use + `Psalm\Codebase::$analysis_php_version_id`. + - [BC] Property `Psalm\Codebase::$php_minor_version` was removed, use + `Psalm\Codebase::$analysis_php_version_id`. + From ba4a25b4837a7b3694b63f594e1f71b29b444013 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 2 Jan 2022 14:53:59 +0200 Subject: [PATCH 06/78] Drop superfluous import --- src/Psalm/Type/Atomic.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 0385879db43..18eb806ccd5 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -9,7 +9,6 @@ use Psalm\Internal\Type\TypeAlias; use Psalm\Internal\Type\TypeAlias\LinkableTypeAlias; use Psalm\Type; -use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; use Psalm\Type\Atomic\TAssertionFalsy; From b419c299e30110b22391c67376610cbd8067a17f Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sun, 19 Dec 2021 11:17:25 +0000 Subject: [PATCH 07/78] Use InvalidScalarArgument only when we can be sure PHP attempts coercion --- .../Statements/Expression/Call/ArgumentAnalyzer.php | 2 +- .../Internal/Type/Comparator/ScalarTypeComparator.php | 11 ++++++++--- tests/AnnotationTest.php | 6 +++--- tests/ArgTest.php | 10 +++++----- tests/ArrayAssignmentTest.php | 4 ++-- tests/CallableTest.php | 2 +- tests/DocblockInheritanceTest.php | 2 +- tests/FunctionCallTest.php | 2 +- tests/IncludeTest.php | 2 +- tests/MagicMethodAnnotationTest.php | 4 ++-- tests/Template/ClassTemplateExtendsTest.php | 2 +- tests/Template/ClassTemplateTest.php | 2 +- tests/Template/FunctionTemplateTest.php | 2 +- 13 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 84b777c7e20..ee64255efe8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -983,7 +983,7 @@ public static function verifyType( ), $statements_analyzer->getSuppressedIssues() ); - } else { + } elseif ($cased_method_id !== 'echo' && $cased_method_id !== 'print') { IssueBuffer::maybeAdd( new ArgumentTypeCoercion( 'Argument ' . ($argument_offset + 1) . $method_identifier . ' expects ' . $param_type->getId() . diff --git a/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php index 12b1dd951da..a8fdce09fc3 100644 --- a/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php @@ -27,6 +27,7 @@ use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNonEmptyLowercaseString; use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString; +use Psalm\Type\Atomic\TNonEmptyScalar; use Psalm\Type\Atomic\TNonEmptyString; use Psalm\Type\Atomic\TNonFalsyString; use Psalm\Type\Atomic\TNonspecificLiteralInt; @@ -297,7 +298,7 @@ public static function isContainedBy( if ($atomic_comparison_result) { $atomic_comparison_result->type_coerced = true; $atomic_comparison_result->type_coerced_from_mixed = true; - $atomic_comparison_result->scalar_type_match_found = true; + $atomic_comparison_result->scalar_type_match_found = !$container_type_part->from_docblock; } return false; @@ -619,7 +620,8 @@ public static function isContainedBy( if ($input_type_part instanceof TNumeric) { if ($container_type_part->isNumericType()) { if ($atomic_comparison_result) { - $atomic_comparison_result->scalar_type_match_found = true; + $atomic_comparison_result->type_coerced = true; + $atomic_comparison_result->scalar_type_match_found = !$container_type_part->from_docblock; } } } @@ -630,7 +632,10 @@ public static function isContainedBy( && !$container_type_part instanceof TLiteralFloat ) { if ($atomic_comparison_result) { - $atomic_comparison_result->scalar_type_match_found = true; + $atomic_comparison_result->type_coerced + = $atomic_comparison_result->type_coerced_from_scalar + = ($input_type_part instanceof TScalar || $input_type_part instanceof TNonEmptyScalar); + $atomic_comparison_result->scalar_type_match_found = !$container_type_part->from_docblock; } } } diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index 9c65da93b99..c72d1edc367 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -1864,7 +1864,7 @@ function foo(&...$s) : void {} $a = 1; foo($a);', - 'error_message' => 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'spreadOperatorByRefAnnotationBadCall2' => [ ' 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'spreadOperatorByRefAnnotationBadCall3' => [ ' 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'identifyReturnType' => [ ' [ + 'allowArrayIntScalarForArrayStringWithArgumentTypeCoercionIgnored' => [ ' $arr */ + /** @param array $arr */ function foo(array $arr) : void { } @@ -171,10 +171,10 @@ function bar() : array { return []; } - /** @psalm-suppress InvalidScalarArgument */ + /** @psalm-suppress ArgumentTypeCoercion */ foo(bar());', ], - 'allowArrayScalarForArrayStringWithScalarIgnored' => [ + 'allowArrayScalarForArrayStringWithArgumentTypeCoercionIgnored' => [ ' $arr */ function foo(array $arr) : void {} @@ -184,7 +184,7 @@ function bar() : array { return []; } - /** @psalm-suppress InvalidScalarArgument */ + /** @psalm-suppress ArgumentTypeCoercion */ foo(bar());', ], 'unpackObjectlikeListArgs' => [ diff --git a/tests/ArrayAssignmentTest.php b/tests/ArrayAssignmentTest.php index 5a7c7f6c1ec..e465b392981 100644 --- a/tests/ArrayAssignmentTest.php +++ b/tests/ArrayAssignmentTest.php @@ -1887,7 +1887,7 @@ function takesArray(array $arr) : void {} $a[] = 2; takesArray($a);', - 'error_message' => 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'listUsedAsArrayWrongListType' => [ ' 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'nonEmptyAssignmentToListElementChangeType' => [ ' 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'inexistantCallableinCallableString' => [ 'boo([1, 2]);', - 'error_message' => 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], ]; } diff --git a/tests/FunctionCallTest.php b/tests/FunctionCallTest.php index 4835ce61e91..ba66e993c9c 100644 --- a/tests/FunctionCallTest.php +++ b/tests/FunctionCallTest.php @@ -1941,7 +1941,7 @@ function a($b): int } a(["a" => "hello"]);', - 'error_message' => 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'objectLikeKeyChecksAgainstDifferentTKeyedArray' => [ ' [ getcwd() . DIRECTORY_SEPARATOR . 'file2.php', ], - 'error_message' => 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'namespacedRequireFunction' => [ 'files' => [ diff --git a/tests/MagicMethodAnnotationTest.php b/tests/MagicMethodAnnotationTest.php index f961664c170..11c5fdf53c9 100644 --- a/tests/MagicMethodAnnotationTest.php +++ b/tests/MagicMethodAnnotationTest.php @@ -813,7 +813,7 @@ class Child extends ParentClass {} $child = new Child(); $child->setString("five");', - 'error_message' => 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'unionAnnotationInvalidArg' => [ 'setBool("hello", 5);', - 'error_message' => 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'validAnnotationWithInvalidVariadicCall' => [ ' 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'extendsTwiceDifferentNameBrokenChain' => [ ' 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'argumentExpectsFleshOutTIndexedAccess' => [ ' 'InvalidScalarArgument', + 'error_message' => 'InvalidArgument', ], 'multipleArgConstraintWithMoreRestrictiveFirstArg' => [ ' Date: Sun, 19 Dec 2021 13:27:31 +0000 Subject: [PATCH 08/78] Add better documentation --- docs/running_psalm/issues/InvalidScalarArgument.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/running_psalm/issues/InvalidScalarArgument.md b/docs/running_psalm/issues/InvalidScalarArgument.md index bc0f97200f9..5a8713736cf 100644 --- a/docs/running_psalm/issues/InvalidScalarArgument.md +++ b/docs/running_psalm/issues/InvalidScalarArgument.md @@ -1,6 +1,10 @@ # InvalidScalarArgument -Emitted when a scalar value is passed to a method that expected another scalar type +Emitted when a scalar value is passed to a method that expected another scalar type. + +This is only emitted in situations where Psalm can be sure that PHP tries to coerce one scalar type to another. + +In all other cases `InvalidArgument` is emitted. ```php Date: Sun, 19 Dec 2021 17:59:48 +0000 Subject: [PATCH 09/78] Add better docs to TypeComparisonResult --- .../Internal/Type/Comparator/TypeComparisonResult.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php b/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php index 5fbe29af824..bdfafc95626 100644 --- a/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php +++ b/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php @@ -7,7 +7,12 @@ class TypeComparisonResult { - /** @var ?bool */ + /** + * This is used to trigger `InvalidScalarArgument` in situations where we know PHP + * will try to coerce one scalar type to another. + * + * @var ?bool + */ public $scalar_type_match_found; /** @var ?bool */ From 4f7c1d05c15201334172695dba81064ea21440e4 Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 20 Oct 2021 20:41:22 +0200 Subject: [PATCH 10/78] remove support for allowPhpstormGenerics --- config.xsd | 8 -- docs/running_psalm/configuration.md | 11 -- src/Psalm/Config.php | 6 - src/Psalm/Internal/Codebase/Populator.php | 100 ------------- src/Psalm/Internal/Type/TypeCombiner.php | 3 +- tests/AnnotationTest.php | 162 ---------------------- 6 files changed, 1 insertion(+), 289 deletions(-) diff --git a/config.xsd b/config.xsd index 9f8676ce823..a79c23f212b 100644 --- a/config.xsd +++ b/config.xsd @@ -51,14 +51,6 @@ - - - - - Deprecated. PHPStorm now supports generics for the most part and @psalm- annotations can be used - - - diff --git a/docs/running_psalm/configuration.md b/docs/running_psalm/configuration.md index 6a84f20742e..03fcd77f44d 100644 --- a/docs/running_psalm/configuration.md +++ b/docs/running_psalm/configuration.md @@ -134,17 +134,6 @@ If true we force strict typing on numerical and string operations (see https://g ``` Setting this to `false` means that any function calls will cause Psalm to forget anything it knew about object properties within the scope of the function it's currently analysing. This duplicates functionality that Hack has. Defaults to `true`. -#### allowPhpStormGenerics - -```xml - -``` -Allows you to specify whether or not to use the typed iterator docblock format supported by PHP Storm e.g. `ArrayIterator|string[]`, which Psalm transforms to `ArrayIterator`. Defaults to `false`. - -This flag is deprecated and will be removed in Psalm 5 - #### allowStringToStandInForClass ```xml diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 9584302113e..3f6579aef5e 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -315,11 +315,6 @@ class Config /** @var bool */ public $use_igbinary = false; - /** - * @var bool - */ - public $allow_phpstorm_generics = false; - /** * @var bool */ @@ -887,7 +882,6 @@ private static function fromXmlAndPaths( 'allowFileIncludes' => 'allow_includes', 'strictBinaryOperands' => 'strict_binary_operands', 'rememberPropertyAssignmentsAfterCall' => 'remember_property_assignments_after_call', - 'allowPhpStormGenerics' => 'allow_phpstorm_generics', 'allowStringToStandInForClass' => 'allow_string_standin_for_class', 'usePhpDocMethodsWithoutMagicCall' => 'use_phpdoc_method_without_magic_or_parent', 'usePhpDocPropertiesWithoutMagicCall' => 'use_phpdoc_property_without_magic_or_parent', diff --git a/src/Psalm/Internal/Codebase/Populator.php b/src/Psalm/Internal/Codebase/Populator.php index c293367f7f6..8199840684e 100644 --- a/src/Psalm/Internal/Codebase/Populator.php +++ b/src/Psalm/Internal/Codebase/Populator.php @@ -14,11 +14,6 @@ use Psalm\Progress\Progress; use Psalm\Storage\ClassLikeStorage; use Psalm\Storage\FileStorage; -use Psalm\Type; -use Psalm\Type\Atomic\TArray; -use Psalm\Type\Atomic\TGenericObject; -use Psalm\Type\Atomic\TIterable; -use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; @@ -110,26 +105,6 @@ public function populateCodebase(): void } foreach ($this->classlike_storage_provider->getNew() as $class_storage) { - if ($this->config->allow_phpstorm_generics) { - foreach ($class_storage->properties as $property_storage) { - if ($property_storage->type) { - $this->convertPhpStormGenericToPsalmGeneric($property_storage->type, true); - } - } - - foreach ($class_storage->methods as $method_storage) { - if ($method_storage->return_type) { - $this->convertPhpStormGenericToPsalmGeneric($method_storage->return_type); - } - - foreach ($method_storage->params as $param_storage) { - if ($param_storage->type) { - $this->convertPhpStormGenericToPsalmGeneric($param_storage->type); - } - } - } - } - foreach ($class_storage->dependent_classlikes as $dependent_classlike_lc => $_) { try { $dependee_storage = $this->classlike_storage_provider->get($dependent_classlike_lc); @@ -141,22 +116,6 @@ public function populateCodebase(): void } } - if ($this->config->allow_phpstorm_generics) { - foreach ($all_file_storage as $file_storage) { - foreach ($file_storage->functions as $function_storage) { - if ($function_storage->return_type) { - $this->convertPhpStormGenericToPsalmGeneric($function_storage->return_type); - } - - foreach ($function_storage->params as $param_storage) { - if ($param_storage->type) { - $this->convertPhpStormGenericToPsalmGeneric($param_storage->type); - } - } - } - } - } - $this->progress->debug('FileStorage is populated' . "\n"); ClassLikeStorageProvider::populated(); @@ -991,65 +950,6 @@ private function populateFileStorage(FileStorage $storage, array $dependent_file $storage->populated = true; } - private function convertPhpStormGenericToPsalmGeneric(Union $candidate, bool $is_property = false): void - { - if (!$candidate->from_docblock) { - //never convert a type that comes from a signature - return; - } - - $atomic_types = $candidate->getAtomicTypes(); - - if (isset($atomic_types['array']) && count($atomic_types) > 1 && !isset($atomic_types['null'])) { - $iterator_name = null; - $generic_params = null; - $iterator_key = null; - - try { - foreach ($atomic_types as $type_key => $type) { - if ($type instanceof TIterable - || ($type instanceof TNamedObject - && (!$type->from_docblock || $is_property) - && ( - strtolower($type->value) === 'traversable' - || $this->classlikes->interfaceExtends( - $type->value, - 'Traversable' - ) - || $this->classlikes->classImplements( - $type->value, - 'Traversable' - ) - )) - ) { - $iterator_name = $type->value; - $iterator_key = $type_key; - } elseif ($type instanceof TArray) { - $generic_params = $type->type_params; - } - } - } catch (InvalidArgumentException $e) { - // ignore class-not-found issues - } - - if ($iterator_name && $iterator_key && $generic_params) { - if ($iterator_name === 'iterable') { - $generic_iterator = new TIterable($generic_params); - } else { - if (strtolower($iterator_name) === 'generator') { - $generic_params[] = Type::getMixed(); - $generic_params[] = Type::getMixed(); - } - $generic_iterator = new TGenericObject($iterator_name, $generic_params); - } - - $candidate->removeType('array'); - $candidate->removeType($iterator_key); - $candidate->addType($generic_iterator); - } - } - } - protected function inheritMethodsFromParent( ClassLikeStorage $storage, ClassLikeStorage $parent_storage diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index 99fbeffff30..f6f0e2d768e 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -180,8 +180,7 @@ public static function combine( && (isset($combination->named_object_types['Traversable']) || isset($combination->builtin_type_params['Traversable'])) && ( - ($codebase && $codebase->config->allow_phpstorm_generics) - || isset($combination->builtin_type_params['Traversable']) + isset($combination->builtin_type_params['Traversable']) || (isset($combination->named_object_types['Traversable']) && $combination->named_object_types['Traversable']->from_docblock) ) diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index 9c65da93b99..62e36d0168a 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -22,149 +22,6 @@ public function setUp(): void $codebase->reportUnusedVariables(); } - public function testPhpStormGenericsWithValidArrayIteratorArgument(): void - { - Config::getInstance()->allow_phpstorm_generics = true; - - $this->addFile( - 'somefile.php', - 'offsetGet("a"); - takesString($s); - - foreach ($i as $s2) { - takesString($s2); - } - }' - ); - - $this->analyzeFile('somefile.php', new Context()); - } - - public function testPhpStormGenericsWithTypeInSignature(): void - { - Config::getInstance()->allow_phpstorm_generics = true; - - $this->addFile( - 'somefile.php', - 'analyzeFile('somefile.php', new Context()); - } - - public function testPhpStormGenericsWithValidTraversableArgument(): void - { - Config::getInstance()->allow_phpstorm_generics = true; - - $this->addFile( - 'somefile.php', - 'analyzeFile('somefile.php', new Context()); - } - - public function testPhpStormGenericsWithClassProperty(): void - { - Config::getInstance()->allow_phpstorm_generics = true; - - $this->addFile( - 'somefile.php', - 'bar; - } - }' - ); - - $this->analyzeFile('somefile.php', new Context()); - } - - public function testPhpStormGenericsWithGeneratorArray(): void - { - Config::getInstance()->allow_phpstorm_generics = true; - - $this->addFile( - 'somefile.php', - 'analyzeFile('somefile.php', new Context()); - } - - public function testPhpStormGenericsWithValidIterableArgument(): void - { - Config::getInstance()->allow_phpstorm_generics = true; - - $this->addFile( - 'somefile.php', - 'analyzeFile('somefile.php', new Context()); - } - - public function testPhpStormGenericsInvalidArgument(): void - { - $this->expectException(CodeException::class); - $this->expectExceptionMessage('InvalidScalarArgument'); - - Config::getInstance()->allow_phpstorm_generics = true; - - $this->addFile( - 'somefile.php', - 'offsetGet("a"); - takesInt($s); - }' - ); - - $this->analyzeFile('somefile.php', new Context()); - } - public function testLessSpecificImplementedReturnTypeWithDocblockOnMultipleLines(): void { $this->expectException(CodeException::class); @@ -236,25 +93,6 @@ class WTF extends \DateTime { }' $this->analyzeFile('somefile.php', new Context()); } - public function testPhpStormGenericsNoTypehint(): void - { - $this->expectException(CodeException::class); - $this->expectExceptionMessage('PossiblyInvalidMethodCall'); - - Config::getInstance()->allow_phpstorm_generics = true; - - $this->addFile( - 'somefile.php', - 'offsetGet("a"); - }' - ); - - $this->analyzeFile('somefile.php', new Context()); - } - public function testInvalidParamDefault(): void { $this->expectException(CodeException::class); From 48de574777cd1bffa43ea07821addfa98324342a Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 20 Oct 2021 20:49:23 +0200 Subject: [PATCH 11/78] remove support for allowPhpstormGenerics --- src/Psalm/Codebase.php | 1 - src/Psalm/Internal/Codebase/Populator.php | 8 -------- 2 files changed, 9 deletions(-) diff --git a/src/Psalm/Codebase.php b/src/Psalm/Codebase.php index cb72efa8b76..73cf0d1d2ad 100644 --- a/src/Psalm/Codebase.php +++ b/src/Psalm/Codebase.php @@ -384,7 +384,6 @@ public function __construct( ); $this->populator = new Populator( - $config, $providers->classlike_storage_provider, $providers->file_storage_provider, $this->classlikes, diff --git a/src/Psalm/Internal/Codebase/Populator.php b/src/Psalm/Internal/Codebase/Populator.php index 8199840684e..47270665257 100644 --- a/src/Psalm/Internal/Codebase/Populator.php +++ b/src/Psalm/Internal/Codebase/Populator.php @@ -3,7 +3,6 @@ namespace Psalm\Internal\Codebase; use InvalidArgumentException; -use Psalm\Config; use Psalm\Internal\Analyzer\ClassLikeAnalyzer; use Psalm\Internal\MethodIdentifier; use Psalm\Internal\Provider\ClassLikeStorageProvider; @@ -60,18 +59,12 @@ class Populator */ private $classlikes; - /** - * @var Config - */ - private $config; - /** * @var FileReferenceProvider */ private $file_reference_provider; public function __construct( - Config $config, ClassLikeStorageProvider $classlike_storage_provider, FileStorageProvider $file_storage_provider, ClassLikes $classlikes, @@ -82,7 +75,6 @@ public function __construct( $this->file_storage_provider = $file_storage_provider; $this->classlikes = $classlikes; $this->progress = $progress; - $this->config = $config; $this->file_reference_provider = $file_reference_provider; } From b658b2738f73f11789d460e58cc9717494149bfd Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 3 Nov 2021 19:26:07 +0100 Subject: [PATCH 12/78] remove exitFunctions --- config.xsd | 8 --- src/Psalm/Config.php | 14 ---- .../FunctionLike/ReturnTypeAnalyzer.php | 2 - .../FunctionLike/ReturnTypeCollector.php | 2 - .../Analyzer/FunctionLikeAnalyzer.php | 1 - src/Psalm/Internal/Analyzer/ScopeAnalyzer.php | 37 ---------- .../Statements/Block/IfElse/ElseAnalyzer.php | 1 - .../Block/IfElse/ElseIfAnalyzer.php | 1 - .../Statements/Block/IfElse/IfAnalyzer.php | 1 - .../Statements/Block/IfElseAnalyzer.php | 1 - .../Statements/Block/LoopAnalyzer.php | 2 - .../Statements/Block/SwitchAnalyzer.php | 4 -- .../Analyzer/Statements/Block/TryAnalyzer.php | 3 - .../Reflector/FunctionLikeNodeScanner.php | 1 - tests/Config/ConfigTest.php | 67 ------------------- 15 files changed, 145 deletions(-) diff --git a/config.xsd b/config.xsd index 9f8676ce823..9511be473ea 100644 --- a/config.xsd +++ b/config.xsd @@ -16,14 +16,6 @@ - - - - - Deprecated. Replaced by documenting never as a return type. It is going to be removed in Psalm 5. - - - diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 9584302113e..d72bd6ee90f 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -502,13 +502,6 @@ class Config /** @var ClassLoader|null */ private $composer_class_loader; - /** - * Custom functions that always exit - * - * @var array - */ - public $exit_functions = []; - /** * @var string */ @@ -1114,13 +1107,6 @@ private static function fromXmlAndPaths( } } - if (isset($config_xml->exitFunctions) && isset($config_xml->exitFunctions->function)) { - /** @var SimpleXMLElement $exit_function */ - foreach ($config_xml->exitFunctions->function as $exit_function) { - $config->exit_functions[strtolower((string) $exit_function['name'])] = true; - } - } - if (isset($config_xml->stubs) && isset($config_xml->stubs->file)) { /** @var SimpleXMLElement $stub_file */ foreach ($config_xml->stubs->file as $stub_file) { diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php index 2566009bcba..bc8d2e08d46 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php @@ -156,7 +156,6 @@ public static function verifyReturnType( && ScopeAnalyzer::getControlActions( $function_stmts, $type_provider, - $codebase->config->exit_functions, [] ) !== [ScopeAnalyzer::ACTION_END] && !$inferred_yield_types @@ -177,7 +176,6 @@ public static function verifyReturnType( $control_actions = ScopeAnalyzer::getControlActions( $function_stmts, $type_provider, - $codebase->config->exit_functions, [], false ); diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php index f0ab1bc8447..a3fc6cb8085 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php @@ -31,8 +31,6 @@ class ReturnTypeCollector * @return list a list of return types * * @psalm-suppress ComplexMethod to be refactored - * - * TODO: This would probably benefit from using the list of exit_functions */ public static function getReturnTypes( Codebase $codebase, diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 99a11cbb337..3891dfe37f1 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -533,7 +533,6 @@ public function analyze( $final_actions = ScopeAnalyzer::getControlActions( $this->function->getStmts() ?: [], null, - $codebase->config->exit_functions, [] ); diff --git a/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php b/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php index d15b91bbf9e..9fa0567f79c 100644 --- a/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php @@ -14,7 +14,6 @@ use function array_values; use function count; use function in_array; -use function strtolower; /** * @internal @@ -67,7 +66,6 @@ public static function doesEverBreak(array $stmts): bool /** * @param array $stmts - * @param array $exit_functions * @param list<'loop'|'switch'> $break_types * @param bool $return_is_exit Exit and Throw statements are treated differently from return if this is false * @@ -78,7 +76,6 @@ public static function doesEverBreak(array $stmts): bool public static function getControlActions( array $stmts, ?NodeDataProvider $nodes, - array $exit_functions, array $break_types, bool $return_is_exit = true ): array { @@ -109,32 +106,6 @@ public static function getControlActions( return array_values(array_unique(array_merge($control_actions, [self::ACTION_END]))); } - if ($exit_functions) { - if ($stmt->expr instanceof PhpParser\Node\Expr\FuncCall - || $stmt->expr instanceof PhpParser\Node\Expr\StaticCall - ) { - if ($stmt->expr instanceof PhpParser\Node\Expr\FuncCall) { - /** @var string|null */ - $resolved_name = $stmt->expr->name->getAttribute('resolvedName'); - - if ($resolved_name && isset($exit_functions[strtolower($resolved_name)])) { - return array_values(array_unique(array_merge($control_actions, [self::ACTION_END]))); - } - } elseif ($stmt->expr->class instanceof PhpParser\Node\Name - && $stmt->expr->name instanceof PhpParser\Node\Identifier - ) { - /** @var string|null */ - $resolved_class_name = $stmt->expr->class->getAttribute('resolvedName'); - - if ($resolved_class_name - && isset($exit_functions[strtolower($resolved_class_name . '::' . $stmt->expr->name)]) - ) { - return array_values(array_unique(array_merge($control_actions, [self::ACTION_END]))); - } - } - } - } - continue; } @@ -174,7 +145,6 @@ public static function getControlActions( $if_statement_actions = self::getControlActions( $stmt->stmts, $nodes, - $exit_functions, $break_types, $return_is_exit ); @@ -190,7 +160,6 @@ function ($action) { ? self::getControlActions( $stmt->else->stmts, $nodes, - $exit_functions, $break_types, $return_is_exit ) : []; @@ -211,7 +180,6 @@ function ($action) { $elseif_control_actions = self::getControlActions( $elseif->stmts, $nodes, - $exit_functions, $break_types, $return_is_exit ); @@ -268,7 +236,6 @@ function ($action) { $case_actions = self::getControlActions( $case->stmts, $nodes, - $exit_functions, array_merge($break_types, ['switch']), $return_is_exit ); @@ -334,7 +301,6 @@ function ($action) { $loop_actions = self::getControlActions( $stmt->stmts, $nodes, - $exit_functions, array_merge($break_types, ['loop']), $return_is_exit ); @@ -393,7 +359,6 @@ function ($action) { $try_statement_actions = self::getControlActions( $stmt->stmts, $nodes, - $exit_functions, $break_types, $return_is_exit ); @@ -414,7 +379,6 @@ function ($action) { $catch_actions = self::getControlActions( $catch->stmts, $nodes, - $exit_functions, $break_types, $return_is_exit ); @@ -453,7 +417,6 @@ function ($action) { $finally_statement_actions = self::getControlActions( $stmt->finally->stmts, $nodes, - $exit_functions, $break_types, $return_is_exit ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php index f2071b329ea..a9789d9d996 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php @@ -154,7 +154,6 @@ public static function analyze( ? ScopeAnalyzer::getControlActions( $else->stmts, $statements_analyzer->node_data, - $codebase->config->exit_functions, [] ) : [ScopeAnalyzer::ACTION_NONE]; diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php index 105bf206255..4a9476ce57b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php @@ -312,7 +312,6 @@ function (array $carry, Clause $clause): array { $final_actions = ScopeAnalyzer::getControlActions( $elseif->stmts, $statements_analyzer->node_data, - $codebase->config->exit_functions, [] ); // has a return/throw at end diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php index 005a2722db2..f0bd64bceac 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php @@ -75,7 +75,6 @@ public static function analyze( $final_actions = ScopeAnalyzer::getControlActions( $stmt->stmts, $statements_analyzer->node_data, - $codebase->config->exit_functions, [] ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php index 1f9dcc35d81..7418e43ce61 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php @@ -85,7 +85,6 @@ public static function analyze( $final_actions = ScopeAnalyzer::getControlActions( $stmt->stmts, null, - $codebase->config->exit_functions, [] ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php index 1cfd4023eee..a5ed096112a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php @@ -4,7 +4,6 @@ use PhpParser; use Psalm\CodeLocation; -use Psalm\Config; use Psalm\Context; use Psalm\Exception\ComplicatedExpressionException; use Psalm\Internal\Algebra; @@ -96,7 +95,6 @@ public static function analyze( $final_actions = ScopeAnalyzer::getControlActions( $stmts, $statements_analyzer->node_data, - Config::getInstance()->exit_functions, [] ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php index fd492eba78d..c098dc4aaaa 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php @@ -3,7 +3,6 @@ namespace Psalm\Internal\Analyzer\Statements\Block; use PhpParser; -use Psalm\Config; use Psalm\Context; use Psalm\Internal\Algebra; use Psalm\Internal\Analyzer\ScopeAnalyzer; @@ -73,8 +72,6 @@ public static function analyze( $case_action_map = []; - $config = Config::getInstance(); - // create a map of case statement -> ultimate exit type for ($i = count($stmt->cases) - 1; $i >= 0; --$i) { $case = $stmt->cases[$i]; @@ -82,7 +79,6 @@ public static function analyze( $case_actions = $case_action_map[$i] = ScopeAnalyzer::getControlActions( $case->stmts, $statements_analyzer->node_data, - $config->exit_functions, ['switch'] ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php index 8ecffb85d89..9f2f8428f5c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php @@ -52,7 +52,6 @@ public static function analyze( $catch_actions[$i] = ScopeAnalyzer::getControlActions( $catch->stmts, $statements_analyzer->node_data, - $codebase->config->exit_functions, [] ); $all_catches_leave = $all_catches_leave && !in_array(ScopeAnalyzer::ACTION_NONE, $catch_actions[$i], true); @@ -105,7 +104,6 @@ public static function analyze( $try_block_control_actions = ScopeAnalyzer::getControlActions( $stmt->stmts, $statements_analyzer->node_data, - $codebase->config->exit_functions, [] ); @@ -359,7 +357,6 @@ function (string $fq_catch_class) use ($codebase): TNamedObject { $catch_actions[$i] = ScopeAnalyzer::getControlActions( $catch->stmts, $statements_analyzer->node_data, - $codebase->config->exit_functions, [] ); diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index de938d517b7..137562221de 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -300,7 +300,6 @@ public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = fal $final_actions = ScopeAnalyzer::getControlActions( $function_stmt->stmts, null, - $this->config->exit_functions, [], false ); diff --git a/tests/Config/ConfigTest.php b/tests/Config/ConfigTest.php index ed55e000f87..ab00b72775b 100644 --- a/tests/Config/ConfigTest.php +++ b/tests/Config/ConfigTest.php @@ -869,73 +869,6 @@ class MyMockClass {} $this->analyzeFile($file_path, new Context()); } - public function testExitFunctions(): void - { - $this->project_analyzer = $this->getProjectAnalyzerWithConfig( - TestConfig::loadFromXML( - dirname(__DIR__, 2), - ' - - - - - - - ' - ) - ); - - $file_path = getcwd() . '/src/somefile.php'; - - $this->addFile( - $file_path, - 'analyzeFile($file_path, new Context()); - } - public function testValidThrowInvalidCatch(): void { $this->expectExceptionMessage('InvalidCatch'); From 0747b48d06bb8799a46e63cc4b625579fbd0f0f9 Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 13 Oct 2021 19:37:47 +0200 Subject: [PATCH 13/78] remove TEmpty --- .../type_syntax/atomic_types.md | 2 +- .../plugins/plugins_type_system.md | 2 +- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 2 +- .../Internal/Analyzer/ClassLikeAnalyzer.php | 1 + .../FunctionLike/ReturnTypeAnalyzer.php | 2 +- .../Analyzer/FunctionLikeAnalyzer.php | 2 +- .../Statements/Block/ForeachAnalyzer.php | 6 ++- .../Expression/AssignmentAnalyzer.php | 4 +- .../BinaryOp/ArithmeticOpAnalyzer.php | 13 +++--- .../Call/ArrayFunctionArgumentsAnalyzer.php | 10 ++--- .../Call/FunctionCallReturnTypeFetcher.php | 2 +- .../Call/Method/AtomicMethodCallAnalyzer.php | 2 +- .../Method/MethodCallReturnTypeFetcher.php | 2 +- .../Expression/Call/NewAnalyzer.php | 2 +- .../ExistingAtomicStaticCallAnalyzer.php | 2 +- .../Statements/Expression/CastAnalyzer.php | 2 +- .../Statements/Expression/ExitAnalyzer.php | 2 +- .../Expression/Fetch/ArrayFetchAnalyzer.php | 19 +++++---- .../Statements/Expression/YieldAnalyzer.php | 2 +- .../Analyzer/Statements/ReturnAnalyzer.php | 2 +- .../Analyzer/Statements/ThrowAnalyzer.php | 2 +- .../Analyzer/Statements/UnsetAnalyzer.php | 6 +-- .../Codebase/ConstantTypeResolver.php | 6 +-- .../FunctionDocblockManipulator.php | 2 +- .../PropertyDocblockManipulator.php | 2 +- .../LanguageServer/Server/TextDocument.php | 2 +- .../Type/Comparator/ArrayTypeComparator.php | 5 ++- .../Type/Comparator/AtomicTypeComparator.php | 2 +- .../Type/NegatedAssertionReconciler.php | 1 - .../Type/SimpleAssertionReconciler.php | 41 +++++++++++-------- .../Type/SimpleNegatedAssertionReconciler.php | 8 ++-- src/Psalm/Internal/Type/TypeCombiner.php | 6 +-- src/Psalm/Type.php | 7 ++-- src/Psalm/Type/Atomic.php | 4 +- src/Psalm/Type/Atomic/TAssertionEmpty.php | 36 ++++++++++++++++ src/Psalm/Type/Reconciler.php | 6 +-- src/Psalm/Type/Union.php | 2 +- stubs/CoreGenericFunctions.phpstub | 6 +-- stubs/CoreGenericIterators.phpstub | 2 +- tests/ArrayAssignmentTest.php | 4 +- tests/ArrayFunctionCallTest.php | 12 +++--- tests/ConstantTest.php | 2 +- .../MissingReturnTypeTest.php | 2 +- .../ReturnTypeManipulationTest.php | 2 +- tests/FunctionCallTest.php | 4 +- tests/IntRangeTest.php | 24 +++++++---- tests/MethodCallTest.php | 2 +- .../ReturnTypeProvider/GetObjectVarsTest.php | 2 +- tests/Template/ClassTemplateTest.php | 2 +- tests/TypeCombinationTest.php | 18 ++++---- tests/TypeComparatorTest.php | 8 ++-- tests/TypeReconciliation/ReconcilerTest.php | 2 +- tests/TypeReconciliation/TypeTest.php | 4 +- 53 files changed, 187 insertions(+), 128 deletions(-) create mode 100644 src/Psalm/Type/Atomic/TAssertionEmpty.php diff --git a/docs/annotating_code/type_syntax/atomic_types.md b/docs/annotating_code/type_syntax/atomic_types.md index 5fa957be454..20f61d6f43b 100644 --- a/docs/annotating_code/type_syntax/atomic_types.md +++ b/docs/annotating_code/type_syntax/atomic_types.md @@ -64,7 +64,7 @@ This is the _bottom type_ in PHP's type system, and usually represents a return ### `empty` -A type that's equivalent to a "coming soon" sign. Psalm uses this type when it’s awaiting more information — a good example is the type of the empty array `[]`, which Psalm types as `array`. Psalm treats `empty` in a somewhat similar fashion to `never` when combining types together — `empty|int` becomes `int`, just as `never|string` becomes `string`. +A type that's equivalent to a "coming soon" sign. Psalm uses this type when it’s awaiting more information — a good example is the type of the empty array `[]`, which Psalm types as `array`. Psalm treats `empty` in a somewhat similar fashion to `never` when combining types together — `empty|int` becomes `int`, just as `never|string` becomes `string`. ## Other diff --git a/docs/running_psalm/plugins/plugins_type_system.md b/docs/running_psalm/plugins/plugins_type_system.md index 644cc0c3e45..76361e57f4b 100644 --- a/docs/running_psalm/plugins/plugins_type_system.md +++ b/docs/running_psalm/plugins/plugins_type_system.md @@ -35,7 +35,7 @@ The classes are as follows: `TEmptyMixed` - as above, but empty. Generated for `$x` inside the `if` statement `if (!$x) {...}` when `$x` is `mixed` outside. -`TEmpty` - denotes the `empty` type, used to describe a type corresponding to no value whatsoever. Empty arrays `[]` have the type `array`. +`TEmpty` - denotes the `empty` type, used to describe a type corresponding to no value whatsoever. Empty arrays `[]` have the type `array`. `TIterable` - denotes the [`iterable` type](https://www.php.net/manual/en/language.types.iterable.php) (which can also result from an `is_iterable` check). diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index b4800c82f69..97ec0ed11f5 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1553,7 +1553,7 @@ private function checkForMissingPropertyType( if ($suggested_type && !$suggested_type->isNull()) { $message .= ' - consider ' . str_replace( - ['', ''], + ['', ''], '', (string)$suggested_type ); diff --git a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php index 0b4934e8de0..8fed3939f0b 100644 --- a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php @@ -51,6 +51,7 @@ abstract class ClassLikeAnalyzer extends SourceAnalyzer 'false' => 'false', 'object' => 'object', 'empty' => 'empty', + 'never' => 'never', 'callable' => 'callable', 'array' => 'array', 'iterable' => 'iterable', diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php index e8722b1bfdc..73370b3c230 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php @@ -490,7 +490,7 @@ static function (Union $union_type): bool { return null; } - if ($inferred_return_type->hasMixed() || $inferred_return_type->isEmpty()) { + if ($inferred_return_type->hasMixed()) { if (IssueBuffer::accepts( new MixedInferredReturnType( 'Could not verify return type \'' . $declared_return_type . '\' for ' . diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 11beca2d23d..9c6c841bdd8 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -503,7 +503,7 @@ public function analyze( && !$inferred_return_type->isSingleIntLiteral() && !$inferred_return_type->isSingleStringLiteral() && !$inferred_return_type->isTrue() - && $inferred_return_type->getId() !== 'array' + && $inferred_return_type->getId() !== 'array' ) { $manipulator->makePure(); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index bdcf834154e..7dcc2b0adda 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -49,6 +49,7 @@ use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNull; @@ -505,7 +506,10 @@ public static function checkIteratorType( $invalid_iterator_types[] = $iterator_atomic_type->getKey(); $value_type = Type::getMixed(); - } elseif ($iterator_atomic_type instanceof TObject || $iterator_atomic_type instanceof TMixed) { + } elseif ($iterator_atomic_type instanceof TObject || + $iterator_atomic_type instanceof TMixed || + $iterator_atomic_type instanceof TNever + ) { $has_valid_iterator = true; $value_type = Type::getMixed(); $key_type = Type::getMixed(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 407c6d0441e..80a32d1b20b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -569,7 +569,7 @@ public static function analyze( return false; } - $context->vars_in_scope[$var_id] = Type::getEmpty(); + $context->vars_in_scope[$var_id] = Type::getNever(); $context->inside_assignment = $was_in_assignment; @@ -1024,7 +1024,7 @@ public static function assignByRefParam( $by_ref_out_type->parent_nodes += $existing_type->parent_nodes; } - if ($existing_type->getId() !== 'array') { + if ($existing_type->getId() !== 'array') { $context->vars_in_scope[$var_id] = $by_ref_out_type; if (!($stmt_type = $statements_analyzer->node_data->getType($stmt)) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php index f7f7752c1ef..ce6c56a0653 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php @@ -45,6 +45,7 @@ use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; +use Psalm\Type\Union; use function array_diff_key; use function array_values; use function count; @@ -896,7 +897,7 @@ public static function arithmeticOperation( $result = $operand1 - $operand2; } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Mod) { if ($operand2 === 0) { - return Type::getEmpty(); + return Type::getNever(); } $result = $operand1 % $operand2; @@ -916,7 +917,7 @@ public static function arithmeticOperation( $result = $operand1 >> $operand2; } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Div) { if ($operand2 === 0) { - return Type::getEmpty(); + return Type::getNever(); } $result = $operand1 / $operand2; @@ -1269,9 +1270,11 @@ private static function analyzePowBetweenIntRange( $new_result_type = Type::getInt(true, 0); } elseif ($right_type_part->min_bound === 0 && $right_type_part->max_bound === 0) { $new_result_type = Type::getInt(true, 1); + } elseif ($right_type_part->isNegative()) { + $new_result_type = Type::getFloat(); } else { - //technically could be a float(INF)... - $new_result_type = Type::getEmpty(); + $new_result_type = new Union([new TFloat(), new TLiteralInt(0), new TLiteralInt(1)]); + $new_result_type->from_calculation = true; } } else { //$left_type_part may be a mix of positive, negative and 0 @@ -1305,7 +1308,7 @@ private static function analyzeModBetweenIntRange( if ($right_type_part->min_bound !== null && $right_type_part->min_bound === $right_type_part->max_bound) { //if the second operand is a literal, we can be pretty detailed if ($right_type_part->max_bound === 0) { - $new_result_type = Type::getEmpty(); + $new_result_type = Type::getNever(); } else { if ($left_type_part->isPositiveOrZero()) { if ($right_type_part->isPositive()) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php index 654e9bd829b..656e64dcbef 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php @@ -33,9 +33,9 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TCallable; use Psalm\Type\Atomic\TClosure; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Union; @@ -544,8 +544,8 @@ public static function handleByRefArrayAdjustment( if ($array_atomic_type->count === 0) { $array_atomic_type = new TArray( [ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever]), + new Union([new TNever]), ] ); } else { @@ -561,8 +561,8 @@ public static function handleByRefArrayAdjustment( if ($array_atomic_type->count === 0) { $array_atomic_type = new TArray( [ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever]), + new Union([new TNever]), ] ); } else { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php index af190ce1d20..d8798990b98 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php @@ -144,7 +144,7 @@ public static function fetch( $template_result->lower_bounds[$template_name] = [ 'fn-' . $function_id => [ new TemplateBound( - Type::getEmpty() + Type::getNever() ) ] ]; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php index 33d6d173ac6..ae692999191 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php @@ -571,7 +571,7 @@ private static function handleInvalidClass( case TTemplateParam::class: case TEmptyMixed::class: - case TEmpty::class: + case TNever::class: case TMixed::class: case TNonEmptyMixed::class: case TObject::class: diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php index 44bd32a59b3..09fe3f8ff6a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php @@ -588,7 +588,7 @@ public static function replaceTemplateTypes( } else { $template_result->lower_bounds[$template_type->param_name] = [ ($template_type->defining_class) => [ - new TemplateBound(Type::getEmpty()) + new TemplateBound(Type::getNever()) ] ]; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index 2f9548c4195..90ec3879495 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -516,7 +516,7 @@ function ($bounds) use ($codebase) { ); } else { if ($fq_class_name === 'SplObjectStorage') { - $generic_param_type = Type::getEmpty(); + $generic_param_type = Type::getNever(); } else { $generic_param_type = clone array_values($base_type)[0]; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php index 97435117100..04767d9ff6a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php @@ -522,7 +522,7 @@ private static function getMethodReturnType( } else { $template_result->lower_bounds[$template_type->param_name] = [ ($template_type->defining_class) => [ - new TemplateBound(Type::getEmpty()) + new TemplateBound(Type::getNever()) ] ]; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index 6aee61d6dc9..ac5cc2fdaa5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -232,7 +232,7 @@ public static function analyze( $keyed_array->is_list = true; $permissible_atomic_types[] = $keyed_array; } elseif ($type instanceof TNull) { - $permissible_atomic_types[] = new TArray([Type::getEmpty(), Type::getEmpty()]); + $permissible_atomic_types[] = new TArray([Type::getNever(), Type::getNever()]); } elseif ($type instanceof TArray || $type instanceof TList || $type instanceof TKeyedArray diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php index 308a5e77aff..be1b835ff95 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php @@ -136,7 +136,7 @@ public static function analyze( } } - $statements_analyzer->node_data->setType($stmt, Type::getEmpty()); + $statements_analyzer->node_data->setType($stmt, Type::getNever()); $context->has_returned = true; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index 7a7d167a84c..6a7ffd07794 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -56,7 +56,6 @@ use Psalm\Type\Atomic\TClassConstant; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClassStringMap; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; @@ -67,6 +66,7 @@ use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNull; @@ -553,7 +553,10 @@ public static function getArrayAccessTypeGivenOffset( foreach ($array_type->getAtomicTypes() as $type_string => $type) { $original_type = $type; - if ($type instanceof TMixed || $type instanceof TTemplateParam || $type instanceof TEmpty) { + if ($type instanceof TMixed + || $type instanceof TTemplateParam + || $type instanceof TNever + ) { if (!$type instanceof TTemplateParam || $type->as->isMixed() || !$type->as->isSingle()) { $array_access_type = self::handleMixedArrayAccess( $context, @@ -592,7 +595,7 @@ public static function getArrayAccessTypeGivenOffset( $statements_analyzer->getSuppressedIssues() ); - $array_access_type = new Union([new TEmpty]); + $array_access_type = new Union([new TNever]); } } else { if (!$context->inside_isset && !MethodCallAnalyzer::hasNullsafe($stmt->var)) { @@ -1002,7 +1005,7 @@ public static function replaceOffsetTypeWithInts(Union $offset_type): Union } /** - * @param TMixed|TTemplateParam|TEmpty $type + * @param TMixed|TTemplateParam|TNever $type */ public static function handleMixedArrayAccess( Context $context, @@ -1073,7 +1076,7 @@ public static function handleMixedArrayAccess( return Type::combineUnionTypes( $array_access_type, - Type::getMixed($type instanceof TEmpty) + Type::getMixed($type instanceof TNever) ); } @@ -1118,7 +1121,7 @@ private static function handleArrayAccessOnArray( // ok, type becomes an TKeyedArray $array_type->removeType($type_string); $type = new TKeyedArray([ - $key_values[0] => $from_mixed_array ? Type::getMixed() : Type::getEmpty() + $key_values[0] => $from_mixed_array ? Type::getMixed() : Type::getNever() ]); $type->sealed = $from_empty_array; @@ -1525,7 +1528,7 @@ private static function handleArrayAccessOnKeyedArray( clone $type->properties[$key_value] ); } elseif ($in_assignment) { - $type->properties[$key_value] = new Union([new TEmpty]); + $type->properties[$key_value] = new Union([new TNever]); $array_access_type = Type::combineUnionTypes( $array_access_type, @@ -1948,7 +1951,7 @@ private static function handleArrayAccessOnString( $valid_offset_type = Type::getInt(false, 0); } elseif ($type instanceof TLiteralString) { if ($type->value === '') { - $valid_offset_type = Type::getEmpty(); + $valid_offset_type = Type::getNever(); } elseif (strlen($type->value) < 10) { $valid_offsets = []; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php index d539c3ef467..98b7cc8a542 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php @@ -138,7 +138,7 @@ public static function analyze( $expression_type = Type::getMixed(); } } else { - $expression_type = Type::getEmpty(); + $expression_type = Type::getNever(); } $yield_type = null; diff --git a/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php index 6936fc33e14..64fe69b8935 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php @@ -178,7 +178,7 @@ public static function analyze( $statements_analyzer->getSuppressedIssues() ); - $stmt_type = Type::getEmpty(); + $stmt_type = Type::getNever(); } if ($stmt_type->isVoid()) { diff --git a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php index 665a1c40d4d..fbe259dfeff 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php @@ -85,7 +85,7 @@ public static function analyze( } if ($stmt instanceof PhpParser\Node\Expr\Throw_) { - $statements_analyzer->node_data->setType($stmt, Type::getEmpty()); + $statements_analyzer->node_data->setType($stmt, Type::getNever()); } return true; diff --git a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php index c7cfeb6d5a3..47da7c91362 100644 --- a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php @@ -9,10 +9,10 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyMixed; use Psalm\Type\Union; @@ -84,8 +84,8 @@ public static function analyze( } else { $root_type->addType( new TArray([ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever]), + new Union([new TNever]), ]) ); } diff --git a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php index c27cc523f7c..916ff1de636 100644 --- a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php +++ b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php @@ -185,7 +185,7 @@ public static function resolve( $auto_key = 0; if (!$c->entries) { - return new TArray([Type::getEmpty(), Type::getEmpty()]); + return new TArray([Type::getNever(), Type::getNever()]); } $is_list = true; @@ -255,8 +255,8 @@ public static function resolve( if (empty($properties)) { $resolved_type = new TArray([ - new Union([new TEmpty()]), - new Union([new TEmpty()]), + new Union([new TNever()]), + new Union([new TNever()]), ]); } else { $resolved_type = new TKeyedArray($properties); diff --git a/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php b/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php index 147ba4e7ccf..3e8bbe7ae6d 100644 --- a/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php +++ b/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php @@ -300,7 +300,7 @@ public function setParamType( string $new_type, string $phpdoc_type ): void { - $new_type = str_replace(['', '', ''], '', $new_type); + $new_type = str_replace(['', '', ''], '', $new_type); if ($php_type === 'static') { $php_type = ''; diff --git a/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php b/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php index 0ee9654a462..a8d560758b8 100644 --- a/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php +++ b/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php @@ -154,7 +154,7 @@ public function setType( bool $is_php_compatible, ?string $description = null ): void { - $new_type = str_replace(['', '', ''], '', $new_type); + $new_type = str_replace(['', '', ''], '', $new_type); $this->new_php_type = $php_type; $this->new_phpdoc_type = $phpdoc_type; diff --git a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php index fecbc070cb0..ccd38f4243f 100644 --- a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php +++ b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php @@ -262,7 +262,7 @@ public function hover(TextDocumentIdentifier $textDocument, Position $position): * * @param TextDocumentIdentifier $textDocument The text document * @param Position $position The position - * @psalm-return Promise>|Promise + * @psalm-return Promise>|Promise */ public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise { diff --git a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php index d1b31b89578..fd083d74224 100644 --- a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php @@ -12,6 +12,7 @@ use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Union; @@ -39,8 +40,8 @@ public static function isContainedBy( $is_empty_array = $input_type_part->equals( new TArray([ - new Union([new TEmpty()]), - new Union([new TEmpty()]) + new Union([new TNever()]), + new Union([new TNever()]) ]), false ); diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index b6045f4df91..c7c4fac23b5 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -94,7 +94,7 @@ public static function isContainedBy( return true; } - if ($input_type_part instanceof TNever || $input_type_part instanceof TEmpty) { + if ($input_type_part instanceof TNever) { return true; } diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index f25a516018e..ef19ad510d3 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -77,7 +77,6 @@ public static function reconcile( return $existing_var_type; } - $existing_var_atomic_types = $existing_var_type->getAtomicTypes(); if ($assertion === 'false' && isset($existing_var_atomic_types['bool'])) { diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index 2c6562fc221..2d86e812637 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -19,7 +19,6 @@ use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TCallableString; use Psalm\Type\Atomic\TClassString; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEmptyNumeric; use Psalm\Type\Atomic\TEmptyScalar; @@ -35,6 +34,7 @@ use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNonEmptyLowercaseString; @@ -508,13 +508,20 @@ private static function reconcileIsset( if (empty($existing_var_type->getAtomicTypes())) { $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; - return Type::getEmpty(); + return Type::getNever(); } } - if ($existing_var_type->hasType('empty')) { - $existing_var_type->removeType('empty'); - $existing_var_type->addType(new TMixed($inside_loop)); + if ($inside_loop) { + if ($existing_var_type->hasType('empty')) { + $existing_var_type->removeType('empty'); + $existing_var_type->addType(new TMixed(true)); + } + + if ($existing_var_type->hasType('never')) { + $existing_var_type->removeType('never'); + $existing_var_type->addType(new TMixed(true)); + } } $existing_var_type->from_property = false; @@ -550,7 +557,7 @@ private static function reconcileNonEmptyCountable( if (!$array_atomic_type instanceof TNonEmptyArray || ($array_atomic_type->count < $min_count) ) { - if ($array_atomic_type->getId() === 'array') { + if ($array_atomic_type->getId() === 'array') { $existing_var_type->removeType('array'); } else { $non_empty_array = new TNonEmptyArray( @@ -723,7 +730,7 @@ private static function reconcilePositiveNumeric( $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; - return Type::getEmpty(); + return Type::getNever(); } /** @@ -922,7 +929,7 @@ private static function reconcileString( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1020,7 +1027,7 @@ private static function reconcileInt( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1099,7 +1106,7 @@ private static function reconcileBool( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1174,7 +1181,7 @@ private static function reconcileScalar( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1266,7 +1273,7 @@ private static function reconcileNumeric( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1359,7 +1366,7 @@ private static function reconcileObject( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1416,7 +1423,7 @@ private static function reconcileResource( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1777,7 +1784,7 @@ private static function reconcileTraversable( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1872,7 +1879,7 @@ private static function reconcileArray( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1977,7 +1984,7 @@ private static function reconcileList( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index f3d8591f53f..c1e46ce7ea6 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -18,7 +18,6 @@ use Psalm\Type\Atomic\TCallableArray; use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TCallableString; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TInt; @@ -40,6 +39,7 @@ use Psalm\Type\Atomic\TNonEmptyString; use Psalm\Type\Atomic\TNonFalsyString; use Psalm\Type\Atomic\TNonspecificLiteralString; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNumeric; use Psalm\Type\Atomic\TPositiveInt; use Psalm\Type\Atomic\TScalar; @@ -483,14 +483,14 @@ private static function reconcileNonEmptyCountable( $did_remove_type = true; $existing_var_type->removeType('array'); - } elseif ($array_atomic_type->getId() !== 'array') { + } elseif ($array_atomic_type->getId() !== 'array') { $did_remove_type = true; if (!$min_count) { $existing_var_type->addType(new TArray( [ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever()]), + new Union([new TNever()]), ] )); } diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index 99fbeffff30..6708939f1a6 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -9,6 +9,7 @@ use Psalm\Type\Atomic\Scalar; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; +use Psalm\Type\Atomic\TAssertionEmpty; use Psalm\Type\Atomic\TBool; use Psalm\Type\Atomic\TCallable; use Psalm\Type\Atomic\TCallableArray; @@ -17,7 +18,6 @@ use Psalm\Type\Atomic\TCallableString; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClassStringMap; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; @@ -336,7 +336,7 @@ public static function combine( $combination->value_types += $combination->named_object_types; } - $has_empty = (int) isset($combination->value_types['empty']); + $has_empty = (int) (isset($combination->value_types['never']) || isset($combination->value_types['empty'])); $has_never = false; foreach ($combination->value_types as $type) { @@ -347,7 +347,7 @@ public static function combine( continue; } - if (($type instanceof TEmpty || $type instanceof TNever) + if (($type instanceof TNever || $type instanceof TAssertionEmpty) && (count($combination->value_types) > 1 || count($new_types)) ) { $has_never = true; diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index e71d91d4faf..aae5e51d5af 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -17,7 +17,6 @@ use Psalm\Type\Atomic\TBool; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClosure; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; @@ -311,7 +310,7 @@ public static function getScalar(): Union */ public static function getEmpty(): Union { - $type = new TEmpty(); + $type = new TNever(); return new Union([$type]); } @@ -378,8 +377,8 @@ public static function getEmptyArray(): Union { $array_type = new TArray( [ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever()]), + new Union([new TNever()]), ] ); diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 18eb806ccd5..d82e7c46840 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -11,6 +11,7 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; +use Psalm\Type\Atomic\TAssertionEmpty; use Psalm\Type\Atomic\TAssertionFalsy; use Psalm\Type\Atomic\TBool; use Psalm\Type\Atomic\TCallable; @@ -25,7 +26,6 @@ use Psalm\Type\Atomic\TClosedResource; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TDependentGetClass; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEmptyNumeric; use Psalm\Type\Atomic\TEmptyScalar; @@ -231,7 +231,7 @@ public static function create( return new TNamedObject($value); case 'empty': - return $analysis_php_version_id !== null ? new TNamedObject($value) : new TEmpty(); + return $analysis_php_version_id !== null ? new TNamedObject($value) : new TAssertionEmpty(); case 'scalar': return $analysis_php_version_id !== null ? new TNamedObject($value) : new TScalar(); diff --git a/src/Psalm/Type/Atomic/TAssertionEmpty.php b/src/Psalm/Type/Atomic/TAssertionEmpty.php new file mode 100644 index 00000000000..1f73af78eed --- /dev/null +++ b/src/Psalm/Type/Atomic/TAssertionEmpty.php @@ -0,0 +1,36 @@ + $aliased_classes + */ + public function toPhpString( + ?string $namespace, + array $aliased_classes, + ?string $this_class, + int $php_major_version, + int $php_minor_version + ): ?string { + return null; + } + + public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + { + return false; + } +} diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index 67a828515a9..f8bf3c370a8 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -23,13 +23,13 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; use Psalm\Type\Atomic\TClassStringMap; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TObject; use Psalm\Type\Atomic\TScalar; @@ -232,7 +232,7 @@ public static function reconcileKeyedTypes( /** @psalm-suppress TypeDoesNotContainType can be empty after removing above */ if (!$result_type_candidate->getAtomicTypes()) { - $result_type_candidate->addType(new TEmpty); + $result_type_candidate->addType(new TNever()); } $orred_type = Type::combineUnionTypes( @@ -654,7 +654,7 @@ private static function getValueForKey( } } elseif ($existing_key_type_part instanceof TClassStringMap) { return Type::getMixed(); - } elseif ($existing_key_type_part instanceof TEmpty + } elseif ($existing_key_type_part instanceof TNever || ($existing_key_type_part instanceof TMixed && $existing_key_type_part->from_loop_isset) ) { diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 5972e03a12a..0406d06cafe 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -1050,7 +1050,7 @@ public function isGenerator(): bool public function isEmpty(): bool { - return isset($this->types['empty']) && count($this->types) === 1; + return isset($this->types['empty']) || isset($this->types['never']); } public function substitute(Union $old_type, ?Union $new_type = null): void diff --git a/stubs/CoreGenericFunctions.phpstub b/stubs/CoreGenericFunctions.phpstub index e629e571fd9..24352529233 100644 --- a/stubs/CoreGenericFunctions.phpstub +++ b/stubs/CoreGenericFunctions.phpstub @@ -136,7 +136,7 @@ function array_flip(array $array) * * @param TArray $array * - * @return (TArray is array ? null : TKey|null) + * @return (TArray is array ? null : TKey|null) * @psalm-pure * @psalm-ignore-nullable-return */ @@ -150,7 +150,7 @@ function key($array) * * @param TArray $array * - * @return (TArray is array ? null : (TArray is non-empty-array ? TKey : TKey|null)) + * @return (TArray is array ? null : (TArray is non-empty-array ? TKey : TKey|null)) * @psalm-pure */ function array_key_first($array) @@ -163,7 +163,7 @@ function array_key_first($array) * * @param TArray $array * - * @return (TArray is array ? null : (TArray is non-empty-array ? TKey : TKey|null)) + * @return (TArray is array ? null : (TArray is non-empty-array ? TKey : TKey|null)) * @psalm-pure */ function array_key_last($array) diff --git a/stubs/CoreGenericIterators.phpstub b/stubs/CoreGenericIterators.phpstub index c20e54de882..039624b35ee 100644 --- a/stubs/CoreGenericIterators.phpstub +++ b/stubs/CoreGenericIterators.phpstub @@ -442,7 +442,7 @@ class DirectoryIterator extends SplFileInfo implements SeekableIterator { } /** - * @template-implements Iterator + * @template-implements Iterator */ class EmptyIterator implements Iterator { /** diff --git a/tests/ArrayAssignmentTest.php b/tests/ArrayAssignmentTest.php index 5a7c7f6c1ec..f5a2bd54026 100644 --- a/tests/ArrayAssignmentTest.php +++ b/tests/ArrayAssignmentTest.php @@ -1008,7 +1008,7 @@ function updateArray(array $arr) : array { $b = (array) null;', 'assertions' => [ '$a' => 'array{0?: int, 1?: string}', - '$b' => 'array', + '$b' => 'array', ], ], 'getOnCoercedArray' => [ @@ -1124,7 +1124,7 @@ function takesList(array $arr) : void {} takesList($a);', 'assertions' => [ - '$a' => 'array' + '$a' => 'array' ], ], 'listCreatedInSingleStatementUsedAsArray' => [ diff --git a/tests/ArrayFunctionCallTest.php b/tests/ArrayFunctionCallTest.php index f504a6f0def..ded0551671f 100644 --- a/tests/ArrayFunctionCallTest.php +++ b/tests/ArrayFunctionCallTest.php @@ -572,8 +572,8 @@ function foo(array $arr) { $rightCount = [1, 2, 3]; assert (1 > count($rightCount));', 'assertions' => [ - '$leftCount' => 'array', - '$rightCount' => 'array', + '$leftCount' => 'array', + '$rightCount' => 'array', ], ], 'arrayEmptyArrayAfterCountLessThanEqualToZero' => [ @@ -585,8 +585,8 @@ function foo(array $arr) { $rightCount = [1, 2, 3]; assert (0 >= count($rightCount));', 'assertions' => [ - '$leftCount' => 'array', - '$rightCount' => 'array', + '$leftCount' => 'array', + '$rightCount' => 'array', ], ], 'arrayNotNonEmptyArrayAfterCountGreaterThanEqualToZero' => [ @@ -774,7 +774,7 @@ function test(): void { } }', 'assertions' => [], - 'error_levels' => ['MixedAssignment', 'MixedArgument', 'MixedArgumentTypeCoercion'], + 'error_levels' => ['MixedAssignment', 'MixedArgument', 'MixedArgumentTypeCoercion', 'NoValue'], ], 'arrayPopNotNullable' => [ ' [ ' $lengths + * @param mixed|array $lengths */ function doStuff($lengths): void { /** @psalm-suppress MixedArgument, MixedAssignment */ diff --git a/tests/ConstantTest.php b/tests/ConstantTest.php index 1aefeb093bc..04139decfe3 100644 --- a/tests/ConstantTest.php +++ b/tests/ConstantTest.php @@ -303,7 +303,7 @@ class C extends B { public const ARR = [...parent::ARR]; } - /** @param array $arg */ + /** @param array $arg */ function foo(array $arg): void {} foo(C::ARR); ', diff --git a/tests/FileManipulation/MissingReturnTypeTest.php b/tests/FileManipulation/MissingReturnTypeTest.php index 4448a04f0fc..7bf42b0d1cc 100644 --- a/tests/FileManipulation/MissingReturnTypeTest.php +++ b/tests/FileManipulation/MissingReturnTypeTest.php @@ -847,7 +847,7 @@ function foo() { }', ' + * @psalm-return array */ function foo(): array { return []; diff --git a/tests/FileManipulation/ReturnTypeManipulationTest.php b/tests/FileManipulation/ReturnTypeManipulationTest.php index 94bacfb1706..415da9b55d5 100644 --- a/tests/FileManipulation/ReturnTypeManipulationTest.php +++ b/tests/FileManipulation/ReturnTypeManipulationTest.php @@ -651,7 +651,7 @@ function get_form_fields(string $a) { /** * @param string $a * - * @psalm-return array + * @psalm-return array */ function get_form_fields(string $a): array { switch($a){ diff --git a/tests/FunctionCallTest.php b/tests/FunctionCallTest.php index 4835ce61e91..5632c5d2bd0 100644 --- a/tests/FunctionCallTest.php +++ b/tests/FunctionCallTest.php @@ -188,7 +188,7 @@ class B extends A { exit; }', 'assertions' => [ - '$a' => 'array', + '$a' => 'array', ], ], 'byRefAfterCallable' => [ @@ -1166,7 +1166,7 @@ function example($x) : int { ' $x + * @param array $x * @return 0 */ function example($x) : int { diff --git a/tests/IntRangeTest.php b/tests/IntRangeTest.php index 5b7cf913eaf..62ec031b628 100644 --- a/tests/IntRangeTest.php +++ b/tests/IntRangeTest.php @@ -203,21 +203,27 @@ function getInt(): int{return 0;} $h = $d % $e; $i = -3 % $a; $j = -3 % $b; + /** @psalm-suppress NoValue */ $k = -3 % $c; $l = -3 % $d; $m = 3 % $a; $n = 3 % $b; + /** @psalm-suppress NoValue */ $o = 3 % $c; $p = 3 % $d; + /** @psalm-suppress NoValue */ $q = $a % 0; $r = $a % 3; $s = $a % -3; + /** @psalm-suppress NoValue */ $t = $b % 0; $u = $b % 3; $v = $b % -3; + /** @psalm-suppress NoValue */ $w = $c % 0; $x = $c % 3; $y = $c % -3; + /** @psalm-suppress NoValue */ $z = $d % 0; $aa = $d % 3; $ab = $d % -3; @@ -228,22 +234,22 @@ function getInt(): int{return 0;} '$h===' => 'int<-4, 4>', '$i===' => 'int', '$j===' => 'int', - '$k===' => 'empty', + '$k===' => 'never', '$l===' => 'int', '$m===' => 'int<0, max>', '$n===' => 'int', - '$o===' => 'empty', + '$o===' => 'never', '$p===' => 'int', - '$q===' => 'empty', + '$q===' => 'never', '$r===' => 'int<0, 2>', '$s===' => 'int<-2, 0>', - '$t===' => 'empty', + '$t===' => 'never', '$u===' => 'int<-2, 0>', '$v===' => 'int<2, 0>', - '$w===' => 'empty', + '$w===' => 'never', '$x===' => 'int<0, 2>', '$y===' => 'int<-2, 0>', - '$z===' => 'empty', + '$z===' => 'never', '$aa===' => 'int<-2, 2>', '$ab===' => 'int<-2, 2>', ] @@ -285,9 +291,9 @@ function getInt(): int{return 0;} ', 'assertions' => [ '$e===' => '0', - '$f===' => 'empty', + '$f===' => 'float', '$g===' => '1', - '$h===' => 'empty', + '$h===' => '0|1|float', '$i===' => 'int', '$j===' => 'float', '$k===' => '-1', @@ -304,7 +310,7 @@ function getInt(): int{return 0;} '$v===' => 'float', '$w===' => '1', '$x===' => '0', - '$y===' => 'empty', + '$y===' => 'float', '$z===' => '1', '$aa===' => 'int<1, max>', '$ab===' => 'float', diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 8676075fd61..4349ce74281 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -935,7 +935,7 @@ final public function getValue() { ' 'SplObjectStorage', + '$a' => 'SplObjectStorage', ] ], 'allowIteratorToBeNull' => [ diff --git a/tests/ReturnTypeProvider/GetObjectVarsTest.php b/tests/ReturnTypeProvider/GetObjectVarsTest.php index 9e8d8500e47..3389bbd4de2 100644 --- a/tests/ReturnTypeProvider/GetObjectVarsTest.php +++ b/tests/ReturnTypeProvider/GetObjectVarsTest.php @@ -33,7 +33,7 @@ class C { } $ret = get_object_vars(new C); ', - ['$ret' => 'array'], + ['$ret' => 'array'], ]; yield 'includesPrivateAndProtectedPropertiesWhenCalledInsideClassScope' => [ diff --git a/tests/Template/ClassTemplateTest.php b/tests/Template/ClassTemplateTest.php index 10204ede0a6..290fc015cd4 100644 --- a/tests/Template/ClassTemplateTest.php +++ b/tests/Template/ClassTemplateTest.php @@ -2506,7 +2506,7 @@ public function add($key, $t) : void { } }', [ - '$a' => 'ArrayCollection' + '$a' => 'ArrayCollection' ] ], 'newGenericBecomesPropertyTypeValidArg' => [ diff --git a/tests/TypeCombinationTest.php b/tests/TypeCombinationTest.php index 1f3350f3bfb..936b747f938 100644 --- a/tests/TypeCombinationTest.php +++ b/tests/TypeCombinationTest.php @@ -124,10 +124,10 @@ public function providerTestValidTypeCombination(): array ], ], 'mixedOrEmptyArray' => [ - 'array|mixed', + 'array|mixed', [ 'mixed', - 'array', + 'array', ], ], 'falseTrueToBool' => [ @@ -197,10 +197,10 @@ public function providerTestValidTypeCombination(): array ], ], 'emptyArrays' => [ - 'array', + 'array', [ - 'array', - 'array', + 'array', + 'array', ], ], 'arrayStringOrEmptyArray' => [ @@ -309,16 +309,16 @@ public function providerTestValidTypeCombination(): array ], ], 'arrayObjectAndParamsWithEmptyArray' => [ - 'ArrayObject|array', + 'ArrayObject|array', [ 'ArrayObject', - 'array', + 'array', ], ], 'emptyArrayWithArrayObjectAndParams' => [ - 'ArrayObject|array', + 'ArrayObject|array', [ - 'array', + 'array', 'ArrayObject', ], ], diff --git a/tests/TypeComparatorTest.php b/tests/TypeComparatorTest.php index 714a786b32e..1092cdfb11f 100644 --- a/tests/TypeComparatorTest.php +++ b/tests/TypeComparatorTest.php @@ -114,19 +114,19 @@ public function getAllowedChildTypes(): array ], 'listAcceptsEmptyArray' => [ 'list', - 'array', + 'array', ], 'arrayAcceptsEmptyArray' => [ 'array', - 'array', + 'array', ], 'arrayOptionalKeyed1AcceptsEmptyArray' => [ 'array{foo?: string}', - 'array', + 'array', ], 'arrayOptionalKeyed2AcceptsEmptyArray' => [ 'array{foo?: string}&array', - 'array', + 'array', ], 'Lowercase-stringAndCallable-string' => [ 'lowercase-string', diff --git a/tests/TypeReconciliation/ReconcilerTest.php b/tests/TypeReconciliation/ReconcilerTest.php index fd1fdb8eba3..4326dc443c8 100644 --- a/tests/TypeReconciliation/ReconcilerTest.php +++ b/tests/TypeReconciliation/ReconcilerTest.php @@ -176,7 +176,7 @@ public function providerTestTypeIsContainedBy(): array 'array>', ], 'objectLikeTypeWithPossiblyUndefinedToEmpty' => [ - 'array', + 'array', 'array{a?: string, b?: string}', ], 'literalNumericStringInt' => [ diff --git a/tests/TypeReconciliation/TypeTest.php b/tests/TypeReconciliation/TypeTest.php index 6908b92dd48..d6470487776 100644 --- a/tests/TypeReconciliation/TypeTest.php +++ b/tests/TypeReconciliation/TypeTest.php @@ -592,7 +592,7 @@ public function barBar(One $one = null) { $ids = []; }', 'assertions' => [ - '$ids' => 'array', + '$ids' => 'array', ], ], 'arrayUnionTypeAssertionWithIsArray' => [ @@ -603,7 +603,7 @@ public function barBar(One $one = null) { $ids = []; }', 'assertions' => [ - '$ids' => 'array', + '$ids' => 'array', ], ], '2dArrayUnionTypeAssertionWithIsArray' => [ From 2358b96f54c327ffd000fa5b9ad6c557f6669bc2 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 15 Dec 2021 11:20:31 +0000 Subject: [PATCH 14/78] Fix merge errors --- .../FunctionLike/ReturnTypeAnalyzer.php | 4 +-- .../BinaryOp/ArithmeticOpAnalyzer.php | 1 - .../Call/Method/AtomicMethodCallAnalyzer.php | 2 +- .../Codebase/ConstantTypeResolver.php | 2 +- .../Stubs/Generator/StubsGenerator.php | 1 - .../Type/Comparator/ArrayTypeComparator.php | 1 - .../Type/Comparator/AtomicTypeComparator.php | 1 - .../Type/SimpleAssertionReconciler.php | 6 ++-- .../Type/SimpleNegatedAssertionReconciler.php | 4 +-- src/Psalm/Internal/Type/TypeExpander.php | 6 ++-- src/Psalm/Type.php | 10 ------ src/Psalm/Type/Atomic/TAssertionEmpty.php | 4 ++- src/Psalm/Type/Atomic/TEmpty.php | 33 ------------------- tests/EnumTest.php | 2 +- tests/TypeReconciliation/ReconcilerTest.php | 2 +- 15 files changed, 16 insertions(+), 63 deletions(-) delete mode 100644 src/Psalm/Type/Atomic/TEmpty.php diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php index 73370b3c230..c1dd9f1c2d1 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php @@ -237,12 +237,12 @@ public static function verifyReturnType( } $number_of_types = count($inferred_return_type_parts); - // we filter TNever and TEmpty that have no bearing on the return type + // we filter TNever that have no bearing on the return type if ($number_of_types > 1) { $inferred_return_type_parts = array_filter( $inferred_return_type_parts, static function (Union $union_type): bool { - return !($union_type->isNever() || $union_type->isEmpty()); + return !$union_type->isNever(); } ); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php index ce6c56a0653..5128a652fc6 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php @@ -45,7 +45,6 @@ use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; -use Psalm\Type\Union; use function array_diff_key; use function array_values; use function count; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php index ae692999191..7231b944ea7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php @@ -27,12 +27,12 @@ use Psalm\Storage\ClassLikeStorage; use Psalm\Type; use Psalm\Type\Atomic; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyMixed; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TObject; diff --git a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php index 916ff1de636..093eacdb895 100644 --- a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php +++ b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php @@ -25,7 +25,6 @@ use Psalm\Type; use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArray; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TLiteralClassString; @@ -33,6 +32,7 @@ use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TString; use Psalm\Type\Atomic\TTrue; diff --git a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php index 19c6266bbca..224b0e5a523 100644 --- a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php @@ -27,7 +27,6 @@ use Psalm\Type\Atomic\TDependentGetDebugType; use Psalm\Type\Atomic\TDependentGetType; use Psalm\Type\Atomic\TDependentListKey; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEmptyNumeric; use Psalm\Type\Atomic\TEmptyScalar; diff --git a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php index fd083d74224..e2857d52bab 100644 --- a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php @@ -7,7 +7,6 @@ use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TClassStringMap; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralInt; diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index c7c4fac23b5..02024fc5b9b 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -15,7 +15,6 @@ use Psalm\Type\Atomic\TClassStringMap; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TConditional; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEnumCase; use Psalm\Type\Atomic\TGenericObject; diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index 2d86e812637..a116ddb5253 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -2272,7 +2272,7 @@ private static function reconcileFalsyOrEmpty( $failed_reconciliation = 2; - return Type::getEmpty(); + return Type::getNever(); } if (!$did_remove_type) { @@ -2304,8 +2304,8 @@ private static function reconcileFalsyOrEmpty( $existing_var_type->removeType('array'); $existing_var_type->addType(new TArray( [ - new Union([new TEmpty()]), - new Union([new TEmpty()]), + new Union([new TNever()]), + new Union([new TNever()]), ] )); } diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index c1e46ce7ea6..7bf2f5f05f2 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -30,6 +30,7 @@ use Psalm\Type\Atomic\TLowercaseString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNonEmptyLowercaseString; @@ -39,7 +40,6 @@ use Psalm\Type\Atomic\TNonEmptyString; use Psalm\Type\Atomic\TNonFalsyString; use Psalm\Type\Atomic\TNonspecificLiteralString; -use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNumeric; use Psalm\Type\Atomic\TPositiveInt; use Psalm\Type\Atomic\TScalar; @@ -714,7 +714,7 @@ private static function reconcileFalsyOrEmpty( $failed_reconciliation = 2; - return Type::getEmpty(); + return Type::getNever(); } if (!$did_remove_type) { diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index 3709534e47e..21875186c39 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -14,7 +14,6 @@ use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TConditional; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntMask; @@ -827,13 +826,12 @@ private static function expandConditional( ); $number_of_types = count($all_conditional_return_types); - // we filter TNever and TEmpty that have no bearing on the return type + // we filter TNever that have no bearing on the return type if ($number_of_types > 1) { $all_conditional_return_types = array_filter( $all_conditional_return_types, static function (Atomic $atomic_type): bool { - return !($atomic_type instanceof TEmpty - || $atomic_type instanceof TNever); + return !$atomic_type instanceof TNever; } ); } diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index aae5e51d5af..19591a88e28 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -305,16 +305,6 @@ public static function getScalar(): Union return new Union([$type]); } - /** - * @deprecated will be removed in Psalm 5. See getNever to retrieve a TNever that replaces TEmpty - */ - public static function getEmpty(): Union - { - $type = new TNever(); - - return new Union([$type]); - } - public static function getNever(): Union { $type = new TNever(); diff --git a/src/Psalm/Type/Atomic/TAssertionEmpty.php b/src/Psalm/Type/Atomic/TAssertionEmpty.php index 1f73af78eed..1b5863608b7 100644 --- a/src/Psalm/Type/Atomic/TAssertionEmpty.php +++ b/src/Psalm/Type/Atomic/TAssertionEmpty.php @@ -1,10 +1,12 @@ `. - * @deprecated Will be replaced by TNever when in type context and TAssertionEmpty for assertion context in Psalm 5 - */ -class TEmpty extends Scalar -{ - public function __toString(): string - { - return 'empty'; - } - - public function getKey(bool $include_extra = true): string - { - return 'empty'; - } - - /** - * @param array $aliased_classes - */ - public function toPhpString( - ?string $namespace, - array $aliased_classes, - ?string $this_class, - int $analysis_php_version_id - ): ?string { - return null; - } -} diff --git a/tests/EnumTest.php b/tests/EnumTest.php index 38b58bc204a..4cf234557a0 100644 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -274,7 +274,7 @@ enum Status: int {} $_z = Status::cases(); ', 'assertions' => [ - '$_z===' => 'array', + '$_z===' => 'array', ], [], '8.1', diff --git a/tests/TypeReconciliation/ReconcilerTest.php b/tests/TypeReconciliation/ReconcilerTest.php index 4326dc443c8..f592a750f83 100644 --- a/tests/TypeReconciliation/ReconcilerTest.php +++ b/tests/TypeReconciliation/ReconcilerTest.php @@ -104,7 +104,7 @@ public function providerTestReconcilation(): array 'nullWithSomeClassPipeNull' => ['null', 'null', 'SomeClass|null'], 'nullWithMixed' => ['null', 'null', 'mixed'], - 'falsyWithSomeClass' => ['empty', 'falsy', 'SomeClass'], + 'falsyWithSomeClass' => ['never', 'falsy', 'SomeClass'], 'falsyWithSomeClassPipeFalse' => ['false', 'falsy', 'SomeClass|false'], 'falsyWithSomeClassPipeBool' => ['false', 'falsy', 'SomeClass|bool'], 'falsyWithMixed' => ['empty-mixed', 'falsy', 'mixed'], From 83911c740840378a957dbdd26fff29e7b5cd4d92 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 15 Dec 2021 14:29:06 +0000 Subject: [PATCH 15/78] Remove unnecessary references to empty in TypeCombiner --- docs/annotating_code/type_syntax/atomic_types.md | 13 +++++++------ docs/running_psalm/plugins/plugins_type_system.md | 4 +--- src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php | 1 - src/Psalm/Internal/Type/TypeCombiner.php | 14 +++++--------- src/Psalm/Type/Atomic/TAssertionEmpty.php | 1 + src/Psalm/Type/Union.php | 2 +- stubs/CoreGenericIterators.phpstub | 4 ++-- tests/ArrayFunctionCallTest.php | 4 ++-- tests/TypeCombinationTest.php | 8 ++++---- 9 files changed, 23 insertions(+), 28 deletions(-) diff --git a/docs/annotating_code/type_syntax/atomic_types.md b/docs/annotating_code/type_syntax/atomic_types.md index 20f61d6f43b..28d31d82918 100644 --- a/docs/annotating_code/type_syntax/atomic_types.md +++ b/docs/annotating_code/type_syntax/atomic_types.md @@ -52,19 +52,20 @@ Atomic types are the basic building block of all type information used in Psalm. - `value-of` - `T[K]` -## Top types, bottom types and empty +## Top types, bottom types ### `mixed` This is the _top type_ in PHP's type system, and represents a lack of type information. Psalm warns about `mixed` types when the `totallyTyped` flag is turned on, or when you're on level 1. ### `never` +It can be aliased to `no-return` or `never-return` in docblocks. Note: it replaced the old `empty` type that used to exist in Psalm -This is the _bottom type_ in PHP's type system, and usually represents a return type for a function that can never actually return, such as `die()`, `exit()`, or a function that always throws an exception. It may also be written in docblocks as `no-return` or `never-return`. - -### `empty` - -A type that's equivalent to a "coming soon" sign. Psalm uses this type when it’s awaiting more information — a good example is the type of the empty array `[]`, which Psalm types as `array`. Psalm treats `empty` in a somewhat similar fashion to `never` when combining types together — `empty|int` becomes `int`, just as `never|string` becomes `string`. +This is the _bottom type_ in PHP's type system. It's used to describe a type that has no possible value. It can happen in multiple cases: +- the actual `never` type from PHP 8.1 (can be used in docblocks for older versions). This type can be used as a return type for functions that will never return, either because they always throw exceptions or always exit() +- an union type that have been stripped for all its possible types. (For example, if a variable is `string|int` and we perform a is_bool() check in a condition, the type of the variable in the condition will be `never` as the condition will never be entered) +- it can represent a placeholder for types yet to come — a good example is the type of the empty array `[]`, which Psalm types as `array`, the content of the array is void so it can accept any content +- it can also happen in the same context as the line above for templates that have yet to be defined ## Other diff --git a/docs/running_psalm/plugins/plugins_type_system.md b/docs/running_psalm/plugins/plugins_type_system.md index 76361e57f4b..311bf37df56 100644 --- a/docs/running_psalm/plugins/plugins_type_system.md +++ b/docs/running_psalm/plugins/plugins_type_system.md @@ -27,7 +27,7 @@ The classes are as follows: `TNull` - denotes the `null` type -`TNever` - denotes the `no-return`/`never-return` type for functions that never return, either throwing an exception or terminating (like the builtin `exit()`). +`TNever` - denotes the `no-return`/`never-return` type for functions that never return, either throwing an exception or terminating (like the builtin `exit()`). Also used for union types that can have no possible types (impossible intersections for example). Empty arrays `[]` have the type `array`. `TMixed` - denotes the `mixed` type, used when you don’t know the type of an expression. @@ -35,8 +35,6 @@ The classes are as follows: `TEmptyMixed` - as above, but empty. Generated for `$x` inside the `if` statement `if (!$x) {...}` when `$x` is `mixed` outside. -`TEmpty` - denotes the `empty` type, used to describe a type corresponding to no value whatsoever. Empty arrays `[]` have the type `array`. - `TIterable` - denotes the [`iterable` type](https://www.php.net/manual/en/language.types.iterable.php) (which can also result from an `is_iterable` check). `TResource` - denotes the `resource` type (e.g. a file handle). diff --git a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php index 8fed3939f0b..c7d608722a8 100644 --- a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php @@ -50,7 +50,6 @@ abstract class ClassLikeAnalyzer extends SourceAnalyzer 'bool' => 'bool', 'false' => 'false', 'object' => 'object', - 'empty' => 'empty', 'never' => 'never', 'callable' => 'callable', 'array' => 'array', diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index 6708939f1a6..53cb97d2a2e 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -9,7 +9,6 @@ use Psalm\Type\Atomic\Scalar; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; -use Psalm\Type\Atomic\TAssertionEmpty; use Psalm\Type\Atomic\TBool; use Psalm\Type\Atomic\TCallable; use Psalm\Type\Atomic\TCallableArray; @@ -78,8 +77,8 @@ class TypeCombiner * - so `int + string = int|string` * - so `array + array = array` * - and `array + string = array|string` - * - and `array + array = array` - * - and `array + array = array` + * - and `array + array = array` + * - and `array + array = array` * - and `array + array = array` * * @param non-empty-list $types @@ -336,20 +335,17 @@ public static function combine( $combination->value_types += $combination->named_object_types; } - $has_empty = (int) (isset($combination->value_types['never']) || isset($combination->value_types['empty'])); - $has_never = false; + $has_never = isset($combination->value_types['never']); foreach ($combination->value_types as $type) { if ($type instanceof TMixed && $combination->mixed_from_loop_isset - && (count($combination->value_types) > (1 + $has_empty) || count($new_types) > $has_empty) + && (count($combination->value_types) > (1 + (int) $has_never) || count($new_types) > (int) $has_never) ) { continue; } - if (($type instanceof TNever || $type instanceof TAssertionEmpty) - && (count($combination->value_types) > 1 || count($new_types)) - ) { + if ($type instanceof TNever && (count($combination->value_types) > 1 || count($new_types))) { $has_never = true; continue; } diff --git a/src/Psalm/Type/Atomic/TAssertionEmpty.php b/src/Psalm/Type/Atomic/TAssertionEmpty.php index 1b5863608b7..7b697698c44 100644 --- a/src/Psalm/Type/Atomic/TAssertionEmpty.php +++ b/src/Psalm/Type/Atomic/TAssertionEmpty.php @@ -1,4 +1,5 @@ types['empty']) || isset($this->types['never']); + return $this->isNever(); } public function substitute(Union $old_type, ?Union $new_type = null): void diff --git a/stubs/CoreGenericIterators.phpstub b/stubs/CoreGenericIterators.phpstub index 039624b35ee..34ad308d324 100644 --- a/stubs/CoreGenericIterators.phpstub +++ b/stubs/CoreGenericIterators.phpstub @@ -446,11 +446,11 @@ class DirectoryIterator extends SplFileInfo implements SeekableIterator { */ class EmptyIterator implements Iterator { /** - * @return empty + * @return never */ public function current() {} /** - * @return empty + * @return never */ public function key() {} /** diff --git a/tests/ArrayFunctionCallTest.php b/tests/ArrayFunctionCallTest.php index ded0551671f..39151240442 100644 --- a/tests/ArrayFunctionCallTest.php +++ b/tests/ArrayFunctionCallTest.php @@ -1170,7 +1170,7 @@ function makeArray(): array { return [1, 3]; } ], 'arrayResetEmptyList' => [ ' */ + /** @return list */ function makeArray(): array { return []; } $a = makeArray(); $b = reset($a);', @@ -1246,7 +1246,7 @@ function makeArray(): array { return [1, 3]; } ], 'arrayEndEmptyList' => [ ' */ + /** @return list */ function makeArray(): array { return []; } $a = makeArray(); $b = end($a);', diff --git a/tests/TypeCombinationTest.php b/tests/TypeCombinationTest.php index 936b747f938..03737402753 100644 --- a/tests/TypeCombinationTest.php +++ b/tests/TypeCombinationTest.php @@ -109,10 +109,10 @@ public function providerTestValidTypeCombination(): array 'null', ], ], - 'mixedOrEmpty' => [ + 'mixedOrNever' => [ 'mixed', [ - 'empty', + 'never', 'mixed', ], ], @@ -206,7 +206,7 @@ public function providerTestValidTypeCombination(): array 'arrayStringOrEmptyArray' => [ 'array', [ - 'array', + 'array', 'array', ], ], @@ -227,7 +227,7 @@ public function providerTestValidTypeCombination(): array 'arrayMixedOrEmpty' => [ 'array', [ - 'array', + 'array', 'array', ], ], From d912663da49333b4ab1b4662d9587207a0da1339 Mon Sep 17 00:00:00 2001 From: orklah Date: Fri, 17 Dec 2021 00:42:14 +0100 Subject: [PATCH 16/78] remove isEmpty --- .../Analyzer/Statements/Block/ForeachAnalyzer.php | 4 +--- .../Analyzer/Statements/Block/SwitchAnalyzer.php | 2 +- .../Analyzer/Statements/Block/SwitchCaseAnalyzer.php | 2 +- .../Analyzer/Statements/Expression/ArrayAnalyzer.php | 4 ++-- .../Expression/Assignment/ArrayAssignmentAnalyzer.php | 2 +- .../Statements/Expression/AssignmentAnalyzer.php | 4 ++-- .../Expression/BinaryOp/ArithmeticOpAnalyzer.php | 4 ++-- .../Statements/Expression/Call/ArgumentAnalyzer.php | 2 +- .../Statements/Expression/Call/ArgumentsAnalyzer.php | 2 +- .../Expression/Call/FunctionCallReturnTypeFetcher.php | 8 +++----- .../Expression/Fetch/ArrayFetchAnalyzer.php | 11 +++++------ .../Fetch/InstancePropertyFetchAnalyzer.php | 2 +- .../Statements/Expression/SimpleTypeInferer.php | 4 ++-- src/Psalm/Internal/Codebase/ConstantTypeResolver.php | 2 +- .../ArrayFilterReturnTypeProvider.php | 2 +- .../ArrayMergeReturnTypeProvider.php | 4 ++-- .../ArrayPointerAdjustmentReturnTypeProvider.php | 2 +- .../ReturnTypeProvider/ArrayPopReturnTypeProvider.php | 2 +- .../Internal/Type/Comparator/ArrayTypeComparator.php | 6 +++--- .../Internal/Type/Comparator/AtomicTypeComparator.php | 2 +- .../Type/Comparator/GenericTypeComparator.php | 2 +- .../Internal/Type/Comparator/KeyedArrayComparator.php | 4 ++-- src/Psalm/Internal/Type/SimpleAssertionReconciler.php | 9 +-------- .../Internal/Type/TemplateStandinTypeReplacer.php | 2 +- src/Psalm/Internal/Type/TypeCombiner.php | 6 +++--- .../Internal/TypeVisitor/ContainsLiteralVisitor.php | 2 +- src/Psalm/Type/Atomic/GenericTrait.php | 2 +- src/Psalm/Type/Atomic/TArray.php | 5 +++++ src/Psalm/Type/Reconciler.php | 6 +++--- src/Psalm/Type/Union.php | 5 ----- tests/ArrayAssignmentTest.php | 6 +++--- 31 files changed, 54 insertions(+), 66 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index 7dcc2b0adda..852abdc8f62 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -435,9 +435,7 @@ public static function checkIteratorType( } // if it's an empty array, we cannot iterate over it - if ($iterator_atomic_type instanceof TArray - && $iterator_atomic_type->type_params[1]->isEmpty() - ) { + if ($iterator_atomic_type instanceof TArray && $iterator_atomic_type->isEmptyArray()) { $always_non_empty_array = false; $has_valid_iterator = true; continue; diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php index fd492eba78d..c6e79105081 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php @@ -171,7 +171,7 @@ public static function analyze( ); if (isset($case_vars_in_scope_reconciled[$switch_var_id]) - && $case_vars_in_scope_reconciled[$switch_var_id]->isEmpty() + && $case_vars_in_scope_reconciled[$switch_var_id]->isNever() ) { $all_options_matched = true; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php index d8802a1cd47..3e97c915ba0 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php @@ -591,7 +591,7 @@ private static function handleNonReturningCase( if (!$case->cond && $switch_var_id && isset($case_context->vars_in_scope[$switch_var_id]) - && $case_context->vars_in_scope[$switch_var_id]->isEmpty() + && $case_context->vars_in_scope[$switch_var_id]->isNever() ) { if (IssueBuffer::accepts( new ParadoxicalCondition( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index d5df517ce26..98dc159f6fe 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -547,7 +547,7 @@ private static function handleUnpackedArray( && count($unpacked_atomic_type->type_params) === 2 )) { /** @psalm-suppress PossiblyUndefinedArrayOffset provably true, but Psalm can’t see it */ - if ($unpacked_atomic_type->type_params[1]->isEmpty()) { + if ($unpacked_atomic_type->type_params[1]->isNever()) { continue; } $array_creation_info->can_create_objectlike = false; @@ -579,7 +579,7 @@ private static function handleUnpackedArray( ) ); } elseif ($unpacked_atomic_type instanceof TList) { - if ($unpacked_atomic_type->type_param->isEmpty()) { + if ($unpacked_atomic_type->type_param->isNever()) { continue; } $array_creation_info->can_create_objectlike = false; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php index a2eb2c60f0a..7652589699b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php @@ -739,7 +739,7 @@ private static function analyzeNestedArrayAssignment( return; } - if ($child_stmt_var_type->isEmpty()) { + if ($child_stmt_var_type->isNever()) { $child_stmt_var_type = Type::getEmptyArray(); $statements_analyzer->node_data->setType($child_stmt->var, $child_stmt_var_type); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 80a32d1b20b..9f9814669fc 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -1028,7 +1028,7 @@ public static function assignByRefParam( $context->vars_in_scope[$var_id] = $by_ref_out_type; if (!($stmt_type = $statements_analyzer->node_data->getType($stmt)) - || $stmt_type->isEmpty() + || $stmt_type->isNever() ) { $statements_analyzer->node_data->setType($stmt, clone $by_ref_type); } @@ -1043,7 +1043,7 @@ public static function assignByRefParam( $stmt_type = $statements_analyzer->node_data->getType($stmt); - if (!$stmt_type || $stmt_type->isEmpty()) { + if (!$stmt_type || $stmt_type->isNever()) { $statements_analyzer->node_data->setType($stmt, clone $by_ref_type); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php index 5128a652fc6..037f426ec20 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php @@ -75,9 +75,9 @@ public static function analyze( $right_type = $nodes->getType($right); $config = Config::getInstance(); - if ($left_type && $left_type->isEmpty()) { + if ($left_type && $left_type->isNever()) { $left_type = $right_type; - } elseif ($right_type && $right_type->isEmpty()) { + } elseif ($right_type && $right_type->isNever()) { $right_type = $left_type; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 4a4ac8c3afb..b309443e907 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -1360,7 +1360,7 @@ private static function coerceValueAfterGatekeeperArgument( && $input_atomic_type->value === $param_atomic_type->value ) { foreach ($input_atomic_type->type_params as $i => $type_param) { - if ($type_param->isEmpty() && isset($param_atomic_type->type_params[$i])) { + if ($type_param->isNever() && isset($param_atomic_type->type_params[$i])) { $input_type_changed = true; $input_atomic_type->type_params[$i] = clone $param_atomic_type->type_params[$i]; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php index 1404cb97a45..a585baf22f3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php @@ -1207,7 +1207,7 @@ private static function evaluateArbitraryParam( ); foreach ($context->vars_in_scope[$var_id]->getAtomicTypes() as $type) { - if ($type instanceof TArray && $type->type_params[1]->isEmpty()) { + if ($type instanceof TArray && $type->isEmptyArray()) { $context->vars_in_scope[$var_id]->removeType('array'); $context->vars_in_scope[$var_id]->addType( new TArray( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php index d8798990b98..94005bf8f8f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php @@ -390,14 +390,13 @@ private static function getReturnTypeFromCallMapWithArgs( foreach ($atomic_types['array']->properties as $property) { // empty, never and possibly undefined can't count for min value if (!$property->possibly_undefined - && !$property->isEmpty() && !$property->isNever() ) { $min++; } - //empty and never can't count for max value because we know keys are undefined - if (!$property->isEmpty() && !$property->isNever()) { + //never can't count for max value because we know keys are undefined + if (!$property->isNever()) { $max++; } } @@ -416,8 +415,7 @@ private static function getReturnTypeFromCallMapWithArgs( } if ($atomic_types['array'] instanceof TArray - && $atomic_types['array']->type_params[0]->isEmpty() - && $atomic_types['array']->type_params[1]->isEmpty() + && $atomic_types['array']->isEmptyArray() ) { return Type::getInt(false, 0); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index 6a7ffd07794..09a30c4262b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -1108,10 +1108,9 @@ private static function handleArrayAccessOnArray( if ($in_assignment && $type instanceof TArray - && $type->type_params[0]->isEmpty() - && $type->type_params[1]->isEmpty() + && $type->isEmptyArray() ) { - $from_empty_array = $type->type_params[0]->isEmpty() && $type->type_params[1]->isEmpty(); + $from_empty_array = $type->isEmptyArray(); if (count($key_values) === 1) { $from_mixed_array = $type->type_params[1]->isMixed(); @@ -1243,12 +1242,12 @@ private static function handleArrayAccessOnTArray( ): void { // if we're assigning to an empty array with a key offset, refashion that array if ($in_assignment) { - if ($type->type_params[0]->isEmpty()) { + if ($type->isEmptyArray()) { $type->type_params[0] = $offset_type->isMixed() ? Type::getArrayKey() : $offset_type; } - } elseif (!$type->type_params[0]->isEmpty()) { + } elseif (!$type->isEmptyArray()) { $expected_offset_type = $type->type_params[0]->hasMixed() ? new Union([new TArrayKey]) : $type->type_params[0]; @@ -1369,7 +1368,7 @@ private static function handleArrayAccessOnTArray( $type->type_params[1] ); - if ($array_access_type->isEmpty() + if ($array_access_type->isNever() && !$array_type->hasMixed() && !$in_assignment && !$context->inside_isset diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php index 35fb44b1acd..775c86ceaf9 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php @@ -120,7 +120,7 @@ public static function analyze( return true; } - if ($stmt_var_type->isEmpty()) { + if ($stmt_var_type->isNever()) { if (IssueBuffer::accepts( new MixedPropertyFetch( 'Cannot fetch property on empty var ' . $stmt_var_id, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php index 5a1bb9975ed..0db311700b3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php @@ -697,7 +697,7 @@ private static function handleUnpackedArray( $array_creation_info->property_types[$new_offset] = $property_value; } } elseif ($unpacked_atomic_type instanceof TArray) { - if ($unpacked_atomic_type->type_params[1]->isEmpty()) { + if ($unpacked_atomic_type->isEmptyArray()) { continue; } $array_creation_info->can_create_objectlike = false; @@ -719,7 +719,7 @@ private static function handleUnpackedArray( ) ); } elseif ($unpacked_atomic_type instanceof TList) { - if ($unpacked_atomic_type->type_param->isEmpty()) { + if ($unpacked_atomic_type->type_param->isNever()) { continue; } $array_creation_info->can_create_objectlike = false; diff --git a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php index 093eacdb895..dfc1a7f0d4e 100644 --- a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php +++ b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php @@ -199,7 +199,7 @@ public static function resolve( $visited_constant_ids + [$c_id => true] ); - if ($spread_array instanceof TArray && $spread_array->type_params[1]->isEmpty()) { + if ($spread_array instanceof TArray && $spread_array->isEmptyArray()) { continue; } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php index 6cc324213dd..fa11cf9f74d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php @@ -109,7 +109,7 @@ static function ($keyed_type) use ($statements_source, $context) { $first_arg_array->properties ), static function ($keyed_type) { - return !$keyed_type->isEmpty(); + return !$keyed_type->isNever(); } ); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index 578560b635c..1e038602bc9 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -154,7 +154,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } } } else { - if (!$unpacked_type_part->type_params[0]->isEmpty()) { + if (!$unpacked_type_part->isEmptyArray()) { foreach ($generic_properties as $key => $keyed_type) { $generic_properties[$key] = Type::combineUnionTypes( $keyed_type, @@ -169,7 +169,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } if ($unpacked_type_part instanceof TArray) { - if ($unpacked_type_part->type_params[1]->isEmpty()) { + if ($unpacked_type_part->isEmptyArray()) { continue; } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php index 66d39525603..d0563dbaaca 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php @@ -80,7 +80,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev throw new UnexpectedValueException('This should never happen'); } - if ($value_type->isEmpty()) { + if ($value_type->isNever()) { $value_type = Type::getFalse(); } elseif (($function_id !== 'reset' && $function_id !== 'end') || !$definitely_has_items) { $value_type->addType(new TFalse); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php index 25dd2bd2ccc..594918e2b37 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php @@ -55,7 +55,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($first_arg_array instanceof TArray) { $value_type = clone $first_arg_array->type_params[1]; - if ($value_type->isEmpty()) { + if ($first_arg_array->isEmptyArray()) { return Type::getNull(); } diff --git a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php index e2857d52bab..a090faee460 100644 --- a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php @@ -102,7 +102,7 @@ public static function isContainedBy( if ($container_type_part instanceof TList && $input_type_part instanceof TArray - && $input_type_part->type_params[1]->isEmpty() + && $input_type_part->isEmptyArray() ) { return !$container_type_part instanceof TNonEmptyList; } @@ -225,7 +225,7 @@ function ($i) { continue; } - if ($input_param->isEmpty() + if ($input_param->isNever() && $container_type_part instanceof TNonEmptyArray ) { return false; @@ -233,7 +233,7 @@ function ($i) { $param_comparison_result = new TypeComparisonResult(); - if (!$input_param->isEmpty()) { + if (!$input_param->isNever()) { if (!UnionTypeComparator::isContainedBy( $codebase, $input_param, diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index 02024fc5b9b..ed209cacdf5 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -471,7 +471,7 @@ public static function isContainedBy( $array_comparison_result = new TypeComparisonResult(); - if (!$input_param->isEmpty()) { + if (!$input_param->isNever()) { if (!UnionTypeComparator::isContainedBy( $codebase, $input_param, diff --git a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php index dd7e648c3fd..c63f56dd8c2 100644 --- a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php @@ -94,7 +94,7 @@ public static function isContainedBy( $container_param = $container_type_part->type_params[$i]; - if ($input_param->isEmpty()) { + if ($input_param->isNever()) { if ($atomic_comparison_result) { if (!$atomic_comparison_result->replacement_atomic_type) { $atomic_comparison_result->replacement_atomic_type = clone $input_type_part; diff --git a/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php b/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php index bb42838b7e8..7c372e438b8 100644 --- a/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php +++ b/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php @@ -42,7 +42,7 @@ public static function isContainedBy( $property_type_comparison = new TypeComparisonResult(); - if (!$input_property_type->isEmpty()) { + if (!$input_property_type->isNever()) { if (!UnionTypeComparator::isContainedBy( $codebase, $input_property_type, @@ -127,7 +127,7 @@ public static function isContainedByObjectWithProperties( $property_type_comparison = new TypeComparisonResult(); - if (!$input_property_type->isEmpty() + if (!$input_property_type->isNever() && !UnionTypeComparator::isContainedBy( $codebase, $input_property_type, diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index a116ddb5253..c943f623581 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -513,11 +513,6 @@ private static function reconcileIsset( } if ($inside_loop) { - if ($existing_var_type->hasType('empty')) { - $existing_var_type->removeType('empty'); - $existing_var_type->addType(new TMixed(true)); - } - if ($existing_var_type->hasType('never')) { $existing_var_type->removeType('never'); $existing_var_type->addType(new TMixed(true)); @@ -1932,9 +1927,7 @@ private static function reconcileList( } } - if ($type->type_params[0]->isEmpty() - || $type->type_params[1]->isEmpty() - ) { + if ($type->isEmptyArray()) { //we allow an empty array to pass as a list. We keep the type as empty array though (more precise) $array_types[] = $type; } diff --git a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php index 1dc6033b60e..af4ad25914a 100644 --- a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php +++ b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php @@ -1099,7 +1099,7 @@ function (TemplateBound $bound_a, TemplateBound $bound_b) { if ($current_depth === null) { $current_depth = $template_bound->appearance_depth; } elseif ($current_depth !== $template_bound->appearance_depth && $current_type) { - if (!$current_type->isEmpty() + if (!$current_type->isNever() && ($had_invariant || $last_arg_offset === $template_bound->arg_offset) ) { // escape switches when matching on invariant generic params diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index 53cb97d2a2e..330ce7b8f7e 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -548,7 +548,7 @@ private static function scrapeTypeProperties( $combination->array_always_filled = false; } - if (!$type->type_params[1]->isEmpty()) { + if (!$type->isEmptyArray()) { $combination->all_arrays_lists = false; $combination->all_arrays_class_string_maps = false; } @@ -1326,11 +1326,11 @@ private static function handleKeyedArrayEntries( } if (!$combination->array_type_params - || $combination->array_type_params[1]->isEmpty() + || $combination->array_type_params[1]->isNever() ) { if (!$overwrite_empty_array && ($combination->array_type_params - && ($combination->array_type_params[1]->isEmpty() + && ($combination->array_type_params[1]->isNever() || $combination->array_type_params[1]->isMixed())) ) { foreach ($combination->objectlike_entries as $objectlike_entry) { diff --git a/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php b/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php index e7d3f066031..7c619489711 100644 --- a/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php +++ b/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php @@ -30,7 +30,7 @@ protected function enterNode(TypeNode $type): ?int return NodeVisitor::STOP_TRAVERSAL; } - if ($type instanceof TArray && $type->type_params[1]->isEmpty()) { + if ($type instanceof TArray && $type->isEmptyArray()) { $this->contains_literal = true; return NodeVisitor::STOP_TRAVERSAL; } diff --git a/src/Psalm/Type/Atomic/GenericTrait.php b/src/Psalm/Type/Atomic/GenericTrait.php index cbeb416155e..15d8715334e 100644 --- a/src/Psalm/Type/Atomic/GenericTrait.php +++ b/src/Psalm/Type/Atomic/GenericTrait.php @@ -99,7 +99,7 @@ public function toNamespacedString( $value_type = $this->type_params[1]; - if ($value_type->isMixed() || $value_type->isEmpty()) { + if ($value_type->isMixed() || $value_type->isNever()) { return $base_value; } diff --git a/src/Psalm/Type/Atomic/TArray.php b/src/Psalm/Type/Atomic/TArray.php index eabbe75e72b..cf8ac756c7b 100644 --- a/src/Psalm/Type/Atomic/TArray.php +++ b/src/Psalm/Type/Atomic/TArray.php @@ -91,4 +91,9 @@ public function getAssertionString(bool $exact = false): string return $this->toNamespacedString(null, [], null, false); } + + public function isEmptyArray(): bool + { + return $this->type_params[1]->isNever(); + } } diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index f8bf3c370a8..be5e2e2e146 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -249,7 +249,7 @@ public static function reconcileKeyedTypes( throw new UnexpectedValueException('$result_type should not be null'); } - if (!$did_type_exist && $result_type->isEmpty()) { + if (!$did_type_exist && $result_type->isNever()) { continue; } @@ -1018,7 +1018,7 @@ private static function adjustTKeyedArrayType( foreach ($existing_types[$base_key]->getAtomicTypes() as $base_atomic_type) { if ($base_atomic_type instanceof TKeyedArray || ($base_atomic_type instanceof TArray - && !$base_atomic_type->type_params[1]->isEmpty()) + && !$base_atomic_type->isEmptyArray()) || $base_atomic_type instanceof TList || $base_atomic_type instanceof TClassStringMap ) { @@ -1035,7 +1035,7 @@ private static function adjustTKeyedArrayType( null ); - if (!$previous_key_type->isEmpty()) { + if (!$previous_key_type->isNever()) { $base_atomic_type->previous_key_type = $previous_key_type; } $base_atomic_type->previous_value_type = $previous_value_type; diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 81c2dac16cc..843fd742dd7 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -1048,11 +1048,6 @@ public function isGenerator(): bool && ($single_type->value === 'Generator'); } - public function isEmpty(): bool - { - return $this->isNever(); - } - public function substitute(Union $old_type, ?Union $new_type = null): void { if ($this->hasMixed() && !$this->isEmptyMixed()) { diff --git a/tests/ArrayAssignmentTest.php b/tests/ArrayAssignmentTest.php index f5a2bd54026..e9104c2e1f9 100644 --- a/tests/ArrayAssignmentTest.php +++ b/tests/ArrayAssignmentTest.php @@ -159,7 +159,7 @@ class B {} $foo = []; $foo[][] = "hello";', 'assertions' => [ - '$foo' => 'non-empty-list>', + '$foo' => 'non-empty-list>', ], ], 'implicit3dIntArrayCreation' => [ @@ -167,7 +167,7 @@ class B {} $foo = []; $foo[][][] = "hello";', 'assertions' => [ - '$foo' => 'non-empty-list>>', + '$foo' => 'non-empty-list>>', ], ], 'implicit4dIntArrayCreation' => [ @@ -175,7 +175,7 @@ class B {} $foo = []; $foo[][][][] = "hello";', 'assertions' => [ - '$foo' => 'non-empty-list>>>', + '$foo' => 'non-empty-list>>>', ], ], 'implicitIndexedIntArrayCreation' => [ From 13bb638b60b530f3898bbd2b22df634c2b447fa1 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 2 Jan 2022 15:42:09 +0200 Subject: [PATCH 17/78] Drop remaining `Type::getEmpty()` references --- .../Internal/Type/SimpleNegatedAssertionReconciler.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index 7bf2f5f05f2..f86dfeaf466 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -75,7 +75,7 @@ public static function reconcile( ): ?Union { if ($assertion === 'isset') { if ($existing_var_type->possibly_undefined) { - return Type::getEmpty(); + return Type::getNever(); } if (!$existing_var_type->isNullable() @@ -128,14 +128,14 @@ public static function reconcile( return $existing_var_type->from_docblock ? Type::getNull() - : Type::getEmpty(); + : Type::getNever(); } return Type::getNull(); } if ($assertion === 'array-key-exists') { - return Type::getEmpty(); + return Type::getNever(); } if (strpos($assertion, 'in-array-') === 0) { From 44fbb9fc7792e9b1200863c0efa786791b4f5f03 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 03:50:32 +0200 Subject: [PATCH 18/78] s/array/array/g --- src/Psalm/Type/Atomic.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index d82e7c46840..03dbc2eb7a2 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -789,7 +789,7 @@ public function isFalsy(): bool return true; } - if ($this instanceof TArray && $this->getId() === 'array') { + if ($this instanceof TArray && $this->getId() === 'array') { return true; } From 1bb2661e36308625b5cc2b1582cf24ddfef75e07 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 04:14:48 +0200 Subject: [PATCH 19/78] Update TAssertionEmpty signatures --- src/Psalm/Type/Atomic/TAssertionEmpty.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Type/Atomic/TAssertionEmpty.php b/src/Psalm/Type/Atomic/TAssertionEmpty.php index 7b697698c44..c6a090836f0 100644 --- a/src/Psalm/Type/Atomic/TAssertionEmpty.php +++ b/src/Psalm/Type/Atomic/TAssertionEmpty.php @@ -26,13 +26,12 @@ public function toPhpString( ?string $namespace, array $aliased_classes, ?string $this_class, - int $php_major_version, - int $php_minor_version + int $analysis_php_version_id ): ?string { return null; } - public function canBeFullyExpressedInPhp(int $php_major_version, int $php_minor_version): bool + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { return false; } From c3700e11db0f283dd320a018c7e0ed40a102ef0f Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 06:12:34 +0200 Subject: [PATCH 20/78] Updated UPGRADING.md --- UPGRADING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/UPGRADING.md b/UPGRADING.md index d648e943f68..b18df6329be 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -120,4 +120,8 @@ `Psalm\Codebase::$analysis_php_version_id`. - [BC] Property `Psalm\Codebase::$php_minor_version` was removed, use `Psalm\Codebase::$analysis_php_version_id`. - + - [BC] Class `Psalm\Type\Atomic\TEmpty` was removed + - [BC] Method `Psalm\Type\Union::isEmpty()` was removed + - [BC] Property `Psalm\Config::$allow_phpstorm_generics` was removed + - [BC] Property `Psalm\Config::$exit_functions` was removed + - [BC] Method `Psalm\Type::getEmpty()` was removed From 1c15a6a36cd8d71fabb84f19cdc410873d04d000 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 09:25:06 +0200 Subject: [PATCH 21/78] Disable PSL as not yet compatible with Psalm 5 --- .circleci/config.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index edb010e442f..076772c419e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -68,9 +68,10 @@ jobs: - run: name: Analyse PHPUnit command: bin/test-with-real-projects.sh phpunit - - run: - name: Analyse Psl - command: bin/test-with-real-projects.sh psl + # PSL requires its own plugin that is not yet compatible with Psalm 5 + # - run: + # name: Analyse Psl + # command: bin/test-with-real-projects.sh psl - run: name: Analyse Collections command: bin/test-with-real-projects.sh collections From b54ab67c76e1b98490a9670a0f571f52a39bbb1f Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 08:42:51 +0200 Subject: [PATCH 22/78] Require `@internal` annotation on `Psalm\Internal` symbols --- examples/plugins/InternalChecker.php | 58 ++++++++++++++++++++++++++++ psalm.xml.dist | 1 + 2 files changed, 59 insertions(+) create mode 100644 examples/plugins/InternalChecker.php diff --git a/examples/plugins/InternalChecker.php b/examples/plugins/InternalChecker.php new file mode 100644 index 00000000000..d5a701dcc6d --- /dev/null +++ b/examples/plugins/InternalChecker.php @@ -0,0 +1,58 @@ +getClasslikeStorage(); + if (!$storage->internal + && strpos($storage->name, 'Psalm\\Internal') === 0 + && $storage->location + ) { + IssueBuffer::maybeAdd( + new InternalClass( + "Class $storage->name must be marked @internal", + $storage->location, + $storage->name + ), + $event->getStatementsSource()->getSuppressedIssues(), + true + ); + + if (!$event->getCodebase()->alter_code) { + return null; + } + + $stmt = $event->getStmt(); + $docblock = $stmt->getDocComment(); + if ($docblock) { + $docblock_start = $docblock->getStartFilePos(); + $parsed_docblock = DocComment::parsePreservingLength($docblock); + } else { + $docblock_start = (int) $stmt->getAttribute('startFilePos'); + $parsed_docblock = new ParsedDocblock('', []); + } + $docblock_end = (int) $stmt->getAttribute('startFilePos'); + + $parsed_docblock->tags['internal'] = ['']; + $new_docblock_content = $parsed_docblock->render(''); + $event->setFileReplacements([ + new FileManipulation($docblock_start, $docblock_end, $new_docblock_content) + ]); + } + return null; + } +} diff --git a/psalm.xml.dist b/psalm.xml.dist index cfc85255e5f..4d43b591aff 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -46,6 +46,7 @@ + From b924032850df54dd05ffae409a709820fd6e688e Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 08:55:32 +0200 Subject: [PATCH 23/78] Mark internal classes `@internal` --- psalm-baseline.xml | 322 ++---------------- src/Psalm/Internal/Algebra.php | 3 + .../Internal/Algebra/FormulaGenerator.php | 3 + .../Internal/Analyzer/AttributeAnalyzer.php | 3 + .../Analyzer/ClassLikeNameOptions.php | 3 + .../Internal/Analyzer/DataFlowNodeData.php | 2 + .../FunctionLike/ReturnTypeCollector.php | 2 + src/Psalm/Internal/Analyzer/IssueData.php | 3 + .../Internal/Analyzer/MethodComparator.php | 3 + .../Block/IfConditionalAnalyzer.php | 3 + .../Statements/Block/IfElse/ElseAnalyzer.php | 3 + .../Block/IfElse/ElseIfAnalyzer.php | 3 + .../Statements/Block/IfElse/IfAnalyzer.php | 3 + .../Analyzer/Statements/BreakAnalyzer.php | 3 + .../Analyzer/Statements/ContinueAnalyzer.php | 3 + .../Analyzer/Statements/EchoAnalyzer.php | 3 + .../Expression/ArrayCreationInfo.php | 3 + .../Assignment/AssignedProperty.php | 3 + .../Expression/BitwiseNotAnalyzer.php | 3 + .../Expression/BooleanNotAnalyzer.php | 3 + .../Expression/Call/ArgumentMapPopulator.php | 3 + .../Call/ClassTemplateParamCollector.php | 3 + .../Call/Method/AtomicCallContext.php | 3 + .../Method/AtomicMethodCallAnalysisResult.php | 3 + .../Call/Method/AtomicMethodCallAnalyzer.php | 2 + .../ExistingAtomicMethodCallAnalyzer.php | 3 + .../Method/MethodCallProhibitionAnalyzer.php | 3 + .../Call/Method/MethodCallPurityAnalyzer.php | 3 + .../Method/MethodCallReturnTypeFetcher.php | 3 + .../Call/Method/MethodVisibilityAnalyzer.php | 3 + .../Call/Method/MissingMethodCallHandler.php | 3 + .../StaticMethod/AtomicStaticCallAnalyzer.php | 3 + .../ExistingAtomicStaticCallAnalyzer.php | 3 + .../Statements/Expression/CastAnalyzer.php | 3 + .../Statements/Expression/CloneAnalyzer.php | 3 + .../Statements/Expression/EmptyAnalyzer.php | 3 + .../Expression/EncapsulatedStringAnalyzer.php | 3 + .../Statements/Expression/ExitAnalyzer.php | 3 + .../Expression/ExpressionIdentifier.php | 3 + .../Expression/IncDecExpressionAnalyzer.php | 3 + .../Expression/InstanceofAnalyzer.php | 3 + .../Statements/Expression/IssetAnalyzer.php | 3 + .../Expression/MagicConstAnalyzer.php | 3 + .../Statements/Expression/MatchAnalyzer.php | 3 + .../Statements/Expression/PrintAnalyzer.php | 3 + .../Expression/SimpleTypeInferer.php | 2 + .../Expression/UnaryPlusMinusAnalyzer.php | 3 + .../Statements/Expression/YieldAnalyzer.php | 3 + .../Expression/YieldFromAnalyzer.php | 3 + .../Analyzer/Statements/GlobalAnalyzer.php | 3 + .../Analyzer/Statements/StaticAnalyzer.php | 3 + .../Analyzer/Statements/UnsetAnalyzer.php | 3 + .../Statements/UnusedAssignmentRemover.php | 3 + src/Psalm/Internal/Cli/LanguageServer.php | 3 + src/Psalm/Internal/Cli/Plugin.php | 3 + src/Psalm/Internal/Cli/Psalm.php | 3 + src/Psalm/Internal/Cli/Psalter.php | 3 + src/Psalm/Internal/Cli/Refactor.php | 3 + src/Psalm/Internal/CliUtils.php | 3 + src/Psalm/Internal/Codebase/DataFlowGraph.php | 3 + .../Codebase/ReferenceMapGenerator.php | 3 + .../Internal/Codebase/TaintFlowGraph.php | 3 + .../Internal/Codebase/VariableUseGraph.php | 3 + src/Psalm/Internal/DataFlow/DataFlowNode.php | 2 + src/Psalm/Internal/DataFlow/Path.php | 2 + src/Psalm/Internal/DataFlow/TaintSink.php | 3 + src/Psalm/Internal/DataFlow/TaintSource.php | 3 + src/Psalm/Internal/ErrorHandler.php | 3 + src/Psalm/Internal/EventDispatcher.php | 3 + .../BuildInfoCollector.php | 2 + .../ExecutionEnvironment/GitInfoCollector.php | 2 + .../FileManipulation/CodeMigration.php | 2 + .../Internal/Fork/ForkProcessDoneMessage.php | 2 + .../Internal/Fork/ForkProcessErrorMessage.php | 2 + .../Internal/Fork/ForkTaskDoneMessage.php | 2 + src/Psalm/Internal/Fork/Pool.php | 2 + src/Psalm/Internal/IncludeCollector.php | 2 + src/Psalm/Internal/Json/Json.php | 2 + .../LanguageServer/Client/TextDocument.php | 2 + .../Internal/LanguageServer/IdGenerator.php | 2 + .../LanguageServer/ProtocolStreamReader.php | 2 + .../LanguageServer/Server/TextDocument.php | 2 + .../LanguageServer/Server/Workspace.php | 2 + src/Psalm/Internal/MethodIdentifier.php | 2 + .../Internal/PhpVisitor/CloningVisitor.php | 2 + .../PhpVisitor/ConditionCloningVisitor.php | 3 + .../PhpVisitor/OffsetShifterVisitor.php | 2 + .../PhpVisitor/PartialParserVisitor.php | 2 + .../Reflector/AttributeResolver.php | 3 + .../Reflector/ClassLikeNodeScanner.php | 3 + .../Reflector/ExpressionResolver.php | 3 + .../Reflector/ExpressionScanner.php | 3 + .../Reflector/FunctionLikeDocblockParser.php | 3 + .../Reflector/FunctionLikeDocblockScanner.php | 3 + .../Reflector/FunctionLikeNodeScanner.php | 3 + .../PhpVisitor/Reflector/TypeHintResolver.php | 3 + src/Psalm/Internal/PhpVisitor/TraitFinder.php | 2 + .../PhpVisitor/TypeMappingVisitor.php | 3 + .../Internal/PluginManager/ComposerLock.php | 3 + .../Internal/PluginManager/ConfigFile.php | 3 + .../Internal/PluginManager/PluginList.php | 3 + .../PluginManager/PluginListFactory.php | 3 + .../AddRemoveTaints/HtmlFunctionTainter.php | 3 + .../Internal/Provider/FakeFileProvider.php | 3 + src/Psalm/Internal/Provider/FileProvider.php | 3 + .../Provider/FileReferenceCacheProvider.php | 4 +- .../Provider/FileReferenceProvider.php | 2 + .../Provider/FunctionExistenceProvider.php | 3 + .../Provider/FunctionParamsProvider.php | 3 + .../Provider/FunctionReturnTypeProvider.php | 3 + .../Provider/MethodExistenceProvider.php | 3 + .../Provider/MethodParamsProvider.php | 3 + .../Provider/MethodReturnTypeProvider.php | 3 + .../Provider/MethodVisibilityProvider.php | 3 + .../Internal/Provider/NodeDataProvider.php | 3 + .../Provider/ProjectCacheProvider.php | 2 + .../Provider/PropertyExistenceProvider.php | 3 + .../Provider/PropertyTypeProvider.php | 3 + .../DomDocumentPropertyTypeProvider.php | 3 + .../Provider/PropertyVisibilityProvider.php | 3 + .../ArrayChunkReturnTypeProvider.php | 3 + .../ArrayColumnReturnTypeProvider.php | 3 + .../ArrayFillReturnTypeProvider.php | 3 + .../ArrayFilterReturnTypeProvider.php | 3 + .../ArrayMapReturnTypeProvider.php | 3 + .../ArrayMergeReturnTypeProvider.php | 3 + .../ArrayPadReturnTypeProvider.php | 3 + ...rayPointerAdjustmentReturnTypeProvider.php | 3 + .../ArrayPopReturnTypeProvider.php | 3 + .../ArrayRandReturnTypeProvider.php | 3 + .../ArrayReduceReturnTypeProvider.php | 3 + .../ArrayReverseReturnTypeProvider.php | 3 + .../ArraySliceReturnTypeProvider.php | 3 + .../ArraySpliceReturnTypeProvider.php | 3 + .../ArrayUniqueReturnTypeProvider.php | 3 + .../ArrayValuesReturnTypeProvider.php | 3 + .../ClosureFromCallableReturnTypeProvider.php | 3 + .../ReturnTypeProvider/DomNodeAppendChild.php | 3 + .../ExplodeReturnTypeProvider.php | 3 + .../FilterVarReturnTypeProvider.php | 3 + .../FirstArgStringReturnTypeProvider.php | 3 + .../GetClassMethodsReturnTypeProvider.php | 3 + .../GetObjectVarsReturnTypeProvider.php | 3 + .../HexdecReturnTypeProvider.php | 3 + .../ImagickPixelColorReturnTypeProvider.php | 3 + .../InArrayReturnTypeProvider.php | 3 + .../IteratorToArrayReturnTypeProvider.php | 3 + .../MinMaxReturnTypeProvider.php | 3 + .../MktimeReturnTypeProvider.php | 3 + .../ParseUrlReturnTypeProvider.php | 3 + .../PdoStatementReturnTypeProvider.php | 3 + .../PdoStatementSetFetchMode.php | 3 + .../RandReturnTypeProvider.php | 3 + .../SimpleXmlElementAsXml.php | 3 + .../StrReplaceReturnTypeProvider.php | 3 + .../StrTrReturnTypeProvider.php | 3 + .../TriggerErrorReturnTypeProvider.php | 3 + .../VersionCompareReturnTypeProvider.php | 3 + src/Psalm/Internal/RuntimeCaches.php | 3 + src/Psalm/Internal/Scanner/DocblockParser.php | 2 + src/Psalm/Internal/Scanner/ParsedDocblock.php | 3 + .../UnresolvedConstant/ArrayOffsetFetch.php | 2 + .../UnresolvedConstant/ArraySpread.php | 2 + .../Scanner/UnresolvedConstant/ArrayValue.php | 2 + .../UnresolvedConstant/ClassConstant.php | 2 + .../Scanner/UnresolvedConstant/Constant.php | 2 + .../UnresolvedConstant/KeyValuePair.php | 2 + .../UnresolvedConstant/ScalarValue.php | 2 + .../UnresolvedAdditionOp.php | 2 + .../UnresolvedConstant/UnresolvedBinaryOp.php | 2 + .../UnresolvedBitwiseAnd.php | 2 + .../UnresolvedBitwiseOr.php | 2 + .../UnresolvedBitwiseXor.php | 2 + .../UnresolvedConstant/UnresolvedConcatOp.php | 2 + .../UnresolvedDivisionOp.php | 2 + .../UnresolvedMultiplicationOp.php | 2 + .../UnresolvedSubtractionOp.php | 2 + .../UnresolvedConstant/UnresolvedTernary.php | 2 + .../Scanner/UnresolvedConstantComponent.php | 2 + .../Generator/ClassLikeStubGenerator.php | 3 + .../Stubs/Generator/StubsGenerator.php | 3 + .../Internal/Type/AssertionReconciler.php | 3 + .../Type/Comparator/TypeComparisonResult.php | 3 + .../Type/NegatedAssertionReconciler.php | 3 + .../Type/SimpleAssertionReconciler.php | 2 + .../Type/SimpleNegatedAssertionReconciler.php | 3 + src/Psalm/Internal/Type/TemplateBound.php | 3 + .../Type/TemplateInferredTypeReplacer.php | 3 + src/Psalm/Internal/Type/TemplateResult.php | 2 + .../Type/TemplateStandinTypeReplacer.php | 3 + .../Type/TypeAlias/ClassTypeAlias.php | 3 + .../Type/TypeAlias/InlineTypeAlias.php | 2 + .../Type/TypeAlias/LinkableTypeAlias.php | 2 + src/Psalm/Internal/Type/TypeCombiner.php | 3 + src/Psalm/Internal/Type/TypeParser.php | 3 + src/Psalm/Internal/Type/TypeTokenizer.php | 3 + .../TypeVisitor/ContainsClassLikeVisitor.php | 3 + .../TypeVisitor/ContainsLiteralVisitor.php | 3 + .../TypeVisitor/FromDocblockSetter.php | 3 + .../TypeVisitor/TemplateTypeCollector.php | 3 + .../Internal/TypeVisitor/TypeChecker.php | 3 + .../Internal/TypeVisitor/TypeScanner.php | 3 + 202 files changed, 579 insertions(+), 297 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 66f2bee9590..62e8b0cda67 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + $comment_block->tags['variablesfrom'][0] @@ -13,27 +13,10 @@ - - $this->php_major_version - $this->php_minor_version - $matches[0] $symbol_parts[1] - - $analysis_php_version_id - - - - - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_minor_version - $codebase->php_minor_version - @@ -48,15 +31,6 @@ - - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_minor_version - $codebase->php_minor_version - $codebase->php_minor_version - $comments[0] $stmt->props[0] @@ -68,44 +42,7 @@ $line_parts[1] - - - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_minor_version - $codebase->php_minor_version - - - - - $codebase->php_major_version - $project_analyzer->getCodebase()->php_major_version - $project_analyzer->getCodebase()->php_minor_version - - - - - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_minor_version - $codebase->php_minor_version - - - - $codebase->php_major_version - $codebase->php_minor_version - $this->codebase->php_major_version - $this->codebase->php_major_version - $this->codebase->php_minor_version - $this->codebase->php_minor_version - $destination_parts[1] $destination_parts[1] @@ -145,16 +82,6 @@ $catch_context->assigned_var_ids += $old_catch_assigned_var_ids - - - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_minor_version - $codebase->php_minor_version - - $assertion->rule[0] @@ -193,30 +120,13 @@ $gettype_expr->getArgs()[0] - - - Type::getEmpty() - - - - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - $invalid_left_messages[0] $invalid_right_messages[0] - - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_major_version - $non_existent_method_ids[0] $parts[1] @@ -230,12 +140,6 @@ - - new TEmpty - new TEmpty - new TEmpty - new TEmpty - $args[0] $args[0] @@ -249,31 +153,6 @@ $stmt->getArgs()[0] - - - Type::getEmpty() - - - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_minor_version - - - - - TEmpty::class - - - - - Type::getEmpty() - - - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_minor_version - - $result->invalid_method_call_types[0] @@ -284,21 +163,6 @@ $result->non_existent_magic_method_ids[0] - - - Type::getEmpty() - - - - - Type::getEmpty() - - - $codebase->php_major_version - $codebase->php_major_version - $codebase->php_minor_version - - $assertion->rule[0] @@ -306,66 +170,16 @@ $callable_arg->items[1] - - - Type::getEmpty() - Type::getEmpty() - - - $statements_analyzer->getCodebase()->php_major_version - - - - - Type::getEmpty() - - - - - TMixed|TTemplateParam|TEmpty - new TEmpty - new TEmpty - - - Type::getEmpty() - Type::getEmpty() - - $invalid_fetch_types[0] - - Type::getEmpty() - $atomic_return_type->type_params[2] - - - $statements_analyzer->getCodebase()->php_major_version - $statements_analyzer->getCodebase()->php_minor_version - - - - - Type::getEmpty() - - - - - Type::getEmpty() - - - - - new TEmpty - new TEmpty - - $token_list[$iter] @@ -381,21 +195,7 @@ $stmt->expr->getArgs()[0] - - - new TEmpty() - new TEmpty() - - - Type::getEmpty() - Type::getEmpty() - - - - $codebase->php_major_version - $codebase->php_minor_version - $callables[0] $callables[0] @@ -454,10 +254,6 @@ - - $codebase->php_major_version - $codebase->php_minor_version - $doc_line_parts[1] $matches[0] @@ -466,10 +262,6 @@ - - $this->codebase->php_major_version - $this->codebase->php_minor_version - $imported_type_data[3] $l[4] @@ -477,12 +269,6 @@ $var_line_parts[0] - - - $codebase->php_major_version - $codebase->php_minor_version - - $node->getArgs()[0] @@ -504,83 +290,15 @@ - - $this->codebase->php_major_version - $this->codebase->php_major_version - $this->codebase->php_major_version - $this->codebase->php_major_version - $this->codebase->php_major_version - $this->codebase->php_major_version - $this->codebase->php_major_version - $this->codebase->php_minor_version - $this->codebase->php_minor_version - $this->codebase->php_minor_version - $this->codebase->php_minor_version - $stmt->stmts[0] - - - $this->codebase->php_major_version - - $cs[0] - - - $codebase->php_major_version - $codebase->php_minor_version - - - - - new TEmpty() - new TEmpty() - - - - - $codebase->php_major_version - - - - - new TEmpty() - new TEmpty() - - - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - - - - - new TEmpty - new TEmpty - - - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - Type::getEmpty() - - $combination->array_type_params[1] @@ -634,17 +352,7 @@ $rules[0] - - - new TEmpty - new TEmpty - new TEmpty() - - - - new TEmpty() - array_keys($template_type_map[$value])[0] @@ -655,13 +363,35 @@ - - new TEmpty - $type[0] + + + LanguageServer::run($argv) + + + + + Refactor::run($argv) + + + + + Psalm::run($argv) + + + + + Plugin::run() + + + + + Psalter::run($argv) + + $subNodes['expr'] diff --git a/src/Psalm/Internal/Algebra.php b/src/Psalm/Internal/Algebra.php index f60f76446b1..8f6b08c4dee 100644 --- a/src/Psalm/Internal/Algebra.php +++ b/src/Psalm/Internal/Algebra.php @@ -18,6 +18,9 @@ use function mt_rand; use function substr; +/** + * @internal + */ class Algebra { /** diff --git a/src/Psalm/Internal/Algebra/FormulaGenerator.php b/src/Psalm/Internal/Algebra/FormulaGenerator.php index b6504af7082..66ae5f7fb63 100644 --- a/src/Psalm/Internal/Algebra/FormulaGenerator.php +++ b/src/Psalm/Internal/Algebra/FormulaGenerator.php @@ -19,6 +19,9 @@ use function strlen; use function substr; +/** + * @internal + */ class FormulaGenerator { /** diff --git a/src/Psalm/Internal/Analyzer/AttributeAnalyzer.php b/src/Psalm/Internal/Analyzer/AttributeAnalyzer.php index 3e18d03c1e5..66f1f109163 100644 --- a/src/Psalm/Internal/Analyzer/AttributeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/AttributeAnalyzer.php @@ -21,6 +21,9 @@ use function reset; +/** + * @internal + */ class AttributeAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php b/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php index dff50564c9b..5e5a2cd1af7 100644 --- a/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php +++ b/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php @@ -2,6 +2,9 @@ namespace Psalm\Internal\Analyzer; +/** + * @internal + */ class ClassLikeNameOptions { /** @var bool */ diff --git a/src/Psalm/Internal/Analyzer/DataFlowNodeData.php b/src/Psalm/Internal/Analyzer/DataFlowNodeData.php index a33ac96961f..6973c69f219 100644 --- a/src/Psalm/Internal/Analyzer/DataFlowNodeData.php +++ b/src/Psalm/Internal/Analyzer/DataFlowNodeData.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class DataFlowNodeData { diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php index a3fc6cb8085..824d6c97a34 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php @@ -19,6 +19,8 @@ /** * A class for analysing a given method call's effects in relation to $this/self and also looking at return types + * + * @internal */ class ReturnTypeCollector { diff --git a/src/Psalm/Internal/Analyzer/IssueData.php b/src/Psalm/Internal/Analyzer/IssueData.php index 4ab16f2602e..318afe57f6a 100644 --- a/src/Psalm/Internal/Analyzer/IssueData.php +++ b/src/Psalm/Internal/Analyzer/IssueData.php @@ -6,6 +6,9 @@ use const STR_PAD_LEFT; +/** + * @internal + */ class IssueData { /** diff --git a/src/Psalm/Internal/Analyzer/MethodComparator.php b/src/Psalm/Internal/Analyzer/MethodComparator.php index 1f26cbd43b8..beb533876d7 100644 --- a/src/Psalm/Internal/Analyzer/MethodComparator.php +++ b/src/Psalm/Internal/Analyzer/MethodComparator.php @@ -39,6 +39,9 @@ use function strpos; use function strtolower; +/** + * @internal + */ class MethodComparator { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php index cd4002e511d..79c29b24442 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php @@ -28,6 +28,9 @@ use function array_values; use function count; +/** + * @internal + */ class IfConditionalAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php index a9789d9d996..5944847fdff 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php @@ -23,6 +23,9 @@ use function preg_match; use function preg_quote; +/** + * @internal + */ class ElseAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php index 4a9476ce57b..26f0c3f2670 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php @@ -38,6 +38,9 @@ use function preg_quote; use function spl_object_id; +/** + * @internal + */ class ElseIfAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php index f0bd64bceac..f5e7e79f5fc 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php @@ -38,6 +38,9 @@ use function strpos; use function substr; +/** + * @internal + */ class IfAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php index 2a988563cde..c2bc83491c6 100644 --- a/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php @@ -10,6 +10,9 @@ use function end; +/** + * @internal + */ class BreakAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php index 17826f14e63..864c62ede87 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php @@ -13,6 +13,9 @@ use function end; +/** + * @internal + */ class ContinueAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/EchoAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/EchoAnalyzer.php index 8b9be56b4fa..1cfea01d028 100644 --- a/src/Psalm/Internal/Analyzer/Statements/EchoAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/EchoAnalyzer.php @@ -19,6 +19,9 @@ use Psalm\Type; use Psalm\Type\TaintKind; +/** + * @internal + */ class EchoAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php index ca0e761b283..98a0635cbb4 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php @@ -6,6 +6,9 @@ use Psalm\Type\Atomic; use Psalm\Type\Union; +/** + * @internal + */ class ArrayCreationInfo { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php index 9756e4fc5f7..c1d06eb6fc8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php @@ -4,6 +4,9 @@ use Psalm\Type\Union; +/** + * @internal + */ class AssignedProperty { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php index fa8c19d6ce6..878dc11eca8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php @@ -21,6 +21,9 @@ use Psalm\Type\Atomic\TString; use Psalm\Type\Union; +/** + * @internal + */ class BitwiseNotAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php index cf135ee9d91..fc3d6eb73fd 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php @@ -8,6 +8,9 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Type; +/** + * @internal + */ class BooleanNotAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php index cf5fe6f2d80..ff8c2058ff5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php @@ -18,6 +18,9 @@ use function substr; use function token_get_all; +/** + * @internal + */ class ArgumentMapPopulator { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php index 1450e9e30af..3f05a2a2e67 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php @@ -16,6 +16,9 @@ use function array_merge; use function array_search; +/** + * @internal + */ class ClassTemplateParamCollector { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php index fe0c730e402..8477dd558d3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php @@ -5,6 +5,9 @@ use PhpParser; use Psalm\Internal\MethodIdentifier; +/** + * @internal + */ class AtomicCallContext { /** @var MethodIdentifier */ diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalysisResult.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalysisResult.php index e4d5d5e0eb2..ba2e13db734 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalysisResult.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalysisResult.php @@ -5,6 +5,9 @@ use Psalm\Internal\MethodIdentifier; use Psalm\Type\Union; +/** + * @internal + */ class AtomicMethodCallAnalysisResult { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php index 7231b944ea7..db99b12ecb8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php @@ -56,6 +56,8 @@ * methods. * * The happy path (i.e 99% of method calls) is handled in ExistingAtomicMethodCallAnalyzer + * + * @internal */ class AtomicMethodCallAnalyzer extends CallAnalyzer { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php index 1fda720d0c2..439bce19bf8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php @@ -48,6 +48,9 @@ use function in_array; use function strtolower; +/** + * @internal + */ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php index a3f4660a6ae..255bc8af2e4 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php @@ -11,6 +11,9 @@ use Psalm\Issue\InternalMethod; use Psalm\IssueBuffer; +/** + * @internal + */ class MethodCallProhibitionAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallPurityAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallPurityAnalyzer.php index d0d49fdd706..1b5859bd660 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallPurityAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallPurityAnalyzer.php @@ -19,6 +19,9 @@ use Psalm\Storage\MethodStorage; use Psalm\Type; +/** + * @internal + */ class MethodCallPurityAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php index 09fe3f8ff6a..b7fee9484b0 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php @@ -34,6 +34,9 @@ use function in_array; use function strtolower; +/** + * @internal + */ class MethodCallReturnTypeFetcher { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php index 48ff4dac803..ad6c4fcc41a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php @@ -17,6 +17,9 @@ use function end; use function strtolower; +/** + * @internal + */ class MethodVisibilityAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php index 3b050ce763f..09677820309 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php @@ -25,6 +25,9 @@ use function array_map; use function array_merge; +/** + * @internal + */ class MissingMethodCallHandler { public static function handleMagicMethod( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php index 04b86338104..800e30bfca3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php @@ -60,6 +60,9 @@ use function in_array; use function strtolower; +/** + * @internal + */ class AtomicStaticCallAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php index 04767d9ff6a..f412b89d7b7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php @@ -44,6 +44,9 @@ use function strtolower; use function substr; +/** + * @internal + */ class ExistingAtomicStaticCallAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index ac5cc2fdaa5..e8b67fbb49d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -49,6 +49,9 @@ use function count; use function get_class; +/** + * @internal + */ class CastAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php index 45ed9e9a7f5..efd9fc3dbd1 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php @@ -23,6 +23,9 @@ use function array_merge; use function array_pop; +/** + * @internal + */ class CloneAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php index e874371ff57..429ad9bd1e3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php @@ -11,6 +11,9 @@ use Psalm\IssueBuffer; use Psalm\Type; +/** + * @internal + */ class EmptyAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php index a329ba448a2..d39a94178a0 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php @@ -16,6 +16,9 @@ use function in_array; +/** + * @internal + */ class EncapsulatedStringAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php index be1b835ff95..6892e2b669b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php @@ -21,6 +21,9 @@ use Psalm\Type\TaintKind; use Psalm\Type\Union; +/** + * @internal + */ class ExitAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php index ed779f5a539..6383746d300 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php @@ -14,6 +14,9 @@ use function is_string; use function strtolower; +/** + * @internal + */ class ExpressionIdentifier { public static function getVarId( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php index 38f9a9a3ee5..4ad506e9207 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php @@ -17,6 +17,9 @@ use Psalm\Node\Scalar\VirtualLNumber; use Psalm\Type; +/** + * @internal + */ class IncDecExpressionAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php index e87bfe2fa8e..2c2ba1292c5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php @@ -14,6 +14,9 @@ use function in_array; use function strtolower; +/** + * @internal + */ class InstanceofAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php index 40f33819210..6b5d206946d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php @@ -8,6 +8,9 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Type; +/** + * @internal + */ class IssetAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/MagicConstAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/MagicConstAnalyzer.php index 4432087d9b8..6690096dd2a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/MagicConstAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/MagicConstAnalyzer.php @@ -16,6 +16,9 @@ use Psalm\Type\Atomic\TNonEmptyString; use Psalm\Type\Union; +/** + * @internal + */ class MagicConstAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php index 139fde071d8..d08f44fbccf 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php @@ -40,6 +40,9 @@ use function spl_object_id; use function substr; +/** + * @internal + */ class MatchAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php index b65193abd39..558544a6094 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php @@ -18,6 +18,9 @@ use Psalm\Type; use Psalm\Type\TaintKind; +/** + * @internal + */ class PrintAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php index 0db311700b3..ae4b60eb247 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php @@ -42,6 +42,8 @@ /** * This class takes a statement and return its type by analyzing each part of the statement if necessary + * + * @internal */ class SimpleTypeInferer { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/UnaryPlusMinusAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/UnaryPlusMinusAnalyzer.php index 174854476bb..4bc7a4388d4 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/UnaryPlusMinusAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/UnaryPlusMinusAnalyzer.php @@ -20,6 +20,9 @@ use Psalm\Type\Atomic\TString; use Psalm\Type\Union; +/** + * @internal + */ class UnaryPlusMinusAnalyzer { /** diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php index 98b7cc8a542..4847e04599b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php @@ -22,6 +22,9 @@ use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TNamedObject; +/** + * @internal + */ class YieldAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php index f9c03c41335..16a7cf247f4 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php @@ -14,6 +14,9 @@ use function strtolower; +/** + * @internal + */ class YieldFromAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php index c716bc2bfbc..1781f242d72 100644 --- a/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php @@ -15,6 +15,9 @@ use function is_string; +/** + * @internal + */ class GlobalAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php index 6133afd4509..13152602d6c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php @@ -23,6 +23,9 @@ use function is_string; +/** + * @internal + */ class StaticAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php index 47da7c91362..b11ce758a63 100644 --- a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php @@ -19,6 +19,9 @@ use function count; +/** + * @internal + */ class UnsetAnalyzer { public static function analyze( diff --git a/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php b/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php index 30c01b4e28f..3f6e50e81a7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php +++ b/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php @@ -19,6 +19,9 @@ use function token_get_all; use function trim; +/** + * @internal + */ class UnusedAssignmentRemover { /** diff --git a/src/Psalm/Internal/Cli/LanguageServer.php b/src/Psalm/Internal/Cli/LanguageServer.php index e8689ee968c..94a0d2ad549 100644 --- a/src/Psalm/Internal/Cli/LanguageServer.php +++ b/src/Psalm/Internal/Cli/LanguageServer.php @@ -52,6 +52,9 @@ require_once __DIR__ . '/../Composer.php'; require_once __DIR__ . '/../IncludeCollector.php'; +/** + * @internal + */ final class LanguageServer { /** @param array $argv */ diff --git a/src/Psalm/Internal/Cli/Plugin.php b/src/Psalm/Internal/Cli/Plugin.php index 9fa4ee6ce4d..26ba73c91ce 100644 --- a/src/Psalm/Internal/Cli/Plugin.php +++ b/src/Psalm/Internal/Cli/Plugin.php @@ -22,6 +22,9 @@ require_once __DIR__ . '/../CliUtils.php'; require_once __DIR__ . '/../Composer.php'; +/** + * @internal + */ final class Plugin { public static function run(): void diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 36043f4cf89..6bf2c607fa8 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -87,6 +87,9 @@ require_once __DIR__ . '/../IncludeCollector.php'; require_once __DIR__ . '/../../IssueBuffer.php'; +/** + * @internal + */ final class Psalm { private const SHORT_OPTIONS = [ diff --git a/src/Psalm/Internal/Cli/Psalter.php b/src/Psalm/Internal/Cli/Psalter.php index a29491475cf..d2765a2bd46 100644 --- a/src/Psalm/Internal/Cli/Psalter.php +++ b/src/Psalm/Internal/Cli/Psalter.php @@ -71,6 +71,9 @@ require_once __DIR__ . '/../IncludeCollector.php'; require_once __DIR__ . '/../../IssueBuffer.php'; +/** + * @internal + */ final class Psalter { private const SHORT_OPTIONS = ['f:', 'm', 'h', 'r:', 'c:']; diff --git a/src/Psalm/Internal/Cli/Refactor.php b/src/Psalm/Internal/Cli/Refactor.php index 1c87f4e84a2..a3f147071a7 100644 --- a/src/Psalm/Internal/Cli/Refactor.php +++ b/src/Psalm/Internal/Cli/Refactor.php @@ -56,6 +56,9 @@ require_once __DIR__ . '/../IncludeCollector.php'; require_once __DIR__ . '/../../IssueBuffer.php'; +/** + * @internal + */ final class Refactor { /** @param array $argv */ diff --git a/src/Psalm/Internal/CliUtils.php b/src/Psalm/Internal/CliUtils.php index 05b537ce7db..e6e2f4f2ba8 100644 --- a/src/Psalm/Internal/CliUtils.php +++ b/src/Psalm/Internal/CliUtils.php @@ -48,6 +48,9 @@ use const STDERR; use const STDIN; +/** + * @internal + */ final class CliUtils { public static function requireAutoloaders( diff --git a/src/Psalm/Internal/Codebase/DataFlowGraph.php b/src/Psalm/Internal/Codebase/DataFlowGraph.php index 988a814062f..4a41bf68a3e 100644 --- a/src/Psalm/Internal/Codebase/DataFlowGraph.php +++ b/src/Psalm/Internal/Codebase/DataFlowGraph.php @@ -15,6 +15,9 @@ use function strpos; use function substr; +/** + * @internal + */ abstract class DataFlowGraph { /** @var array> */ diff --git a/src/Psalm/Internal/Codebase/ReferenceMapGenerator.php b/src/Psalm/Internal/Codebase/ReferenceMapGenerator.php index 80a6ad20019..1421846bc1d 100644 --- a/src/Psalm/Internal/Codebase/ReferenceMapGenerator.php +++ b/src/Psalm/Internal/Codebase/ReferenceMapGenerator.php @@ -4,6 +4,9 @@ use Psalm\Internal\Provider\ClassLikeStorageProvider; +/** + * @internal + */ class ReferenceMapGenerator { /** diff --git a/src/Psalm/Internal/Codebase/TaintFlowGraph.php b/src/Psalm/Internal/Codebase/TaintFlowGraph.php index 2812bb6a1fd..f24a20e4634 100644 --- a/src/Psalm/Internal/Codebase/TaintFlowGraph.php +++ b/src/Psalm/Internal/Codebase/TaintFlowGraph.php @@ -41,6 +41,9 @@ use function strlen; use function substr; +/** + * @internal + */ class TaintFlowGraph extends DataFlowGraph { /** @var array */ diff --git a/src/Psalm/Internal/Codebase/VariableUseGraph.php b/src/Psalm/Internal/Codebase/VariableUseGraph.php index 4698f3e2dbc..2e8c192e180 100644 --- a/src/Psalm/Internal/Codebase/VariableUseGraph.php +++ b/src/Psalm/Internal/Codebase/VariableUseGraph.php @@ -10,6 +10,9 @@ use function array_merge; use function count; +/** + * @internal + */ class VariableUseGraph extends DataFlowGraph { /** @var array> */ diff --git a/src/Psalm/Internal/DataFlow/DataFlowNode.php b/src/Psalm/Internal/DataFlow/DataFlowNode.php index 39f45644042..95c2327970d 100644 --- a/src/Psalm/Internal/DataFlow/DataFlowNode.php +++ b/src/Psalm/Internal/DataFlow/DataFlowNode.php @@ -8,6 +8,8 @@ /** * @psalm-consistent-constructor + * + * @internal */ class DataFlowNode { diff --git a/src/Psalm/Internal/DataFlow/Path.php b/src/Psalm/Internal/DataFlow/Path.php index 05d120b4a35..e1fb988508f 100644 --- a/src/Psalm/Internal/DataFlow/Path.php +++ b/src/Psalm/Internal/DataFlow/Path.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class Path { diff --git a/src/Psalm/Internal/DataFlow/TaintSink.php b/src/Psalm/Internal/DataFlow/TaintSink.php index 408f13ccd84..2d89b628d3c 100644 --- a/src/Psalm/Internal/DataFlow/TaintSink.php +++ b/src/Psalm/Internal/DataFlow/TaintSink.php @@ -2,6 +2,9 @@ namespace Psalm\Internal\DataFlow; +/** + * @internal + */ class TaintSink extends DataFlowNode { } diff --git a/src/Psalm/Internal/DataFlow/TaintSource.php b/src/Psalm/Internal/DataFlow/TaintSource.php index c3b9acc6015..747155f7bcb 100644 --- a/src/Psalm/Internal/DataFlow/TaintSource.php +++ b/src/Psalm/Internal/DataFlow/TaintSource.php @@ -2,6 +2,9 @@ namespace Psalm\Internal\DataFlow; +/** + * @internal + */ class TaintSource extends DataFlowNode { } diff --git a/src/Psalm/Internal/ErrorHandler.php b/src/Psalm/Internal/ErrorHandler.php index 802f4727dbc..3d96162d2d1 100644 --- a/src/Psalm/Internal/ErrorHandler.php +++ b/src/Psalm/Internal/ErrorHandler.php @@ -16,6 +16,9 @@ use const E_STRICT; use const STDERR; +/** + * @internal + */ final class ErrorHandler { /** @var bool */ diff --git a/src/Psalm/Internal/EventDispatcher.php b/src/Psalm/Internal/EventDispatcher.php index 82db6300091..3d0791f6c20 100644 --- a/src/Psalm/Internal/EventDispatcher.php +++ b/src/Psalm/Internal/EventDispatcher.php @@ -53,6 +53,9 @@ use function count; use function is_subclass_of; +/** + * @internal + */ class EventDispatcher { /** diff --git a/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php b/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php index 858e7f4b031..6d7ab705dab 100644 --- a/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php +++ b/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php @@ -16,6 +16,8 @@ * Environment variables collector for CI environment. * * @author Kitamura Satoshi + * + * @internal */ class BuildInfoCollector { diff --git a/src/Psalm/Internal/ExecutionEnvironment/GitInfoCollector.php b/src/Psalm/Internal/ExecutionEnvironment/GitInfoCollector.php index e78212859d4..68d3ff77eed 100644 --- a/src/Psalm/Internal/ExecutionEnvironment/GitInfoCollector.php +++ b/src/Psalm/Internal/ExecutionEnvironment/GitInfoCollector.php @@ -19,6 +19,8 @@ * Git repository info collector. * * @author Kitamura Satoshi + * + * @internal */ class GitInfoCollector { diff --git a/src/Psalm/Internal/FileManipulation/CodeMigration.php b/src/Psalm/Internal/FileManipulation/CodeMigration.php index 086af29a667..83e3c79ceed 100644 --- a/src/Psalm/Internal/FileManipulation/CodeMigration.php +++ b/src/Psalm/Internal/FileManipulation/CodeMigration.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class CodeMigration { diff --git a/src/Psalm/Internal/Fork/ForkProcessDoneMessage.php b/src/Psalm/Internal/Fork/ForkProcessDoneMessage.php index edda8ca1d97..4f955b2bde9 100644 --- a/src/Psalm/Internal/Fork/ForkProcessDoneMessage.php +++ b/src/Psalm/Internal/Fork/ForkProcessDoneMessage.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class ForkProcessDoneMessage implements ForkMessage { diff --git a/src/Psalm/Internal/Fork/ForkProcessErrorMessage.php b/src/Psalm/Internal/Fork/ForkProcessErrorMessage.php index 0794c9d83ee..26c620d5001 100644 --- a/src/Psalm/Internal/Fork/ForkProcessErrorMessage.php +++ b/src/Psalm/Internal/Fork/ForkProcessErrorMessage.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class ForkProcessErrorMessage implements ForkMessage { diff --git a/src/Psalm/Internal/Fork/ForkTaskDoneMessage.php b/src/Psalm/Internal/Fork/ForkTaskDoneMessage.php index 7c4322038ae..ccfba7ff107 100644 --- a/src/Psalm/Internal/Fork/ForkTaskDoneMessage.php +++ b/src/Psalm/Internal/Fork/ForkTaskDoneMessage.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class ForkTaskDoneMessage implements ForkMessage { diff --git a/src/Psalm/Internal/Fork/Pool.php b/src/Psalm/Internal/Fork/Pool.php index 3d0ad04ae02..25113319f52 100644 --- a/src/Psalm/Internal/Fork/Pool.php +++ b/src/Psalm/Internal/Fork/Pool.php @@ -67,6 +67,8 @@ * * Fork off to n-processes and divide up tasks between * each process. + * + * @internal */ class Pool { diff --git a/src/Psalm/Internal/IncludeCollector.php b/src/Psalm/Internal/IncludeCollector.php index 6cd24779ebb..617b2b72fb9 100644 --- a/src/Psalm/Internal/IncludeCollector.php +++ b/src/Psalm/Internal/IncludeCollector.php @@ -17,6 +17,8 @@ * Used to execute code that may cause file inclusions, and report what files have been included * NOTE: dependencies of this class should be kept at minimum, as it's used before autoloader is * registered. + * + * @internal */ final class IncludeCollector { diff --git a/src/Psalm/Internal/Json/Json.php b/src/Psalm/Internal/Json/Json.php index 2e2445fed53..ab35c3acd7f 100644 --- a/src/Psalm/Internal/Json/Json.php +++ b/src/Psalm/Internal/Json/Json.php @@ -13,6 +13,8 @@ /** * Provides ability of pretty printed JSON output. + * + * @internal */ class Json { diff --git a/src/Psalm/Internal/LanguageServer/Client/TextDocument.php b/src/Psalm/Internal/LanguageServer/Client/TextDocument.php index 26cd6462fb9..c30aa05ed07 100644 --- a/src/Psalm/Internal/LanguageServer/Client/TextDocument.php +++ b/src/Psalm/Internal/LanguageServer/Client/TextDocument.php @@ -16,6 +16,8 @@ /** * Provides method handlers for all textDocument/* methods + * + * @internal */ class TextDocument { diff --git a/src/Psalm/Internal/LanguageServer/IdGenerator.php b/src/Psalm/Internal/LanguageServer/IdGenerator.php index a7eee0fafab..ebac085c127 100644 --- a/src/Psalm/Internal/LanguageServer/IdGenerator.php +++ b/src/Psalm/Internal/LanguageServer/IdGenerator.php @@ -6,6 +6,8 @@ /** * Generates unique, incremental IDs for use as request IDs + * + * @internal */ class IdGenerator { diff --git a/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php b/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php index 68073df3af4..e1391b4c6eb 100644 --- a/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php +++ b/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php @@ -18,6 +18,8 @@ /** * Source: https://github.com/felixfbecker/php-language-server/tree/master/src/ProtocolStreamReader.php + * + * @internal */ class ProtocolStreamReader implements ProtocolReader { diff --git a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php index ccd38f4243f..cda24184534 100644 --- a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php +++ b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php @@ -36,6 +36,8 @@ /** * Provides method handlers for all textDocument/* methods + * + * @internal */ class TextDocument { diff --git a/src/Psalm/Internal/LanguageServer/Server/Workspace.php b/src/Psalm/Internal/LanguageServer/Server/Workspace.php index c1bc2bd7073..8af7f1cada0 100644 --- a/src/Psalm/Internal/LanguageServer/Server/Workspace.php +++ b/src/Psalm/Internal/LanguageServer/Server/Workspace.php @@ -12,6 +12,8 @@ /** * Provides method handlers for all workspace/* methods + * + * @internal */ class Workspace { diff --git a/src/Psalm/Internal/MethodIdentifier.php b/src/Psalm/Internal/MethodIdentifier.php index cf4c81ba63d..b4336ec3a06 100644 --- a/src/Psalm/Internal/MethodIdentifier.php +++ b/src/Psalm/Internal/MethodIdentifier.php @@ -12,6 +12,8 @@ /** * @psalm-immutable + * + * @internal */ class MethodIdentifier { diff --git a/src/Psalm/Internal/PhpVisitor/CloningVisitor.php b/src/Psalm/Internal/PhpVisitor/CloningVisitor.php index befd1d1adfe..39a03245db1 100644 --- a/src/Psalm/Internal/PhpVisitor/CloningVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/CloningVisitor.php @@ -14,6 +14,8 @@ * Visitor cloning all nodes and linking to the original nodes using an attribute. * * This visitor is required to perform format-preserving pretty prints. + * + * @internal */ class CloningVisitor extends NodeVisitorAbstract { diff --git a/src/Psalm/Internal/PhpVisitor/ConditionCloningVisitor.php b/src/Psalm/Internal/PhpVisitor/ConditionCloningVisitor.php index c368f377b00..6adde5b60f5 100644 --- a/src/Psalm/Internal/PhpVisitor/ConditionCloningVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/ConditionCloningVisitor.php @@ -9,6 +9,9 @@ use PhpParser\NodeVisitorAbstract; use Psalm\Internal\Provider\NodeDataProvider; +/** + * @internal + */ class ConditionCloningVisitor extends NodeVisitorAbstract { private $type_provider; diff --git a/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php b/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php index 7e3de329316..18c680054ef 100644 --- a/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php @@ -6,6 +6,8 @@ /** * Shifts all nodes in a given AST by a set amount + * + * @internal */ class OffsetShifterVisitor extends PhpParser\NodeVisitorAbstract { diff --git a/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php b/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php index 9734be22738..c432272dbba 100644 --- a/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php @@ -25,6 +25,8 @@ /** * Given a list of file diffs, this scans an AST to find the sections it can replace, and parses * just those methods. + * + * @internal */ class PartialParserVisitor extends PhpParser\NodeVisitorAbstract { diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/AttributeResolver.php b/src/Psalm/Internal/PhpVisitor/Reflector/AttributeResolver.php index 9caf12d78c3..16668569308 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/AttributeResolver.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/AttributeResolver.php @@ -17,6 +17,9 @@ use function strtolower; +/** + * @internal + */ class AttributeResolver { public static function resolve( diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php index 7e573e449c1..9e7fa21ebc5 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php @@ -84,6 +84,9 @@ use const PREG_SPLIT_DELIM_CAPTURE; use const PREG_SPLIT_NO_EMPTY; +/** + * @internal + */ class ClassLikeNodeScanner { /** diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php index f4fec865b1c..2d80914b502 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php @@ -37,6 +37,9 @@ use function interface_exists; use function strtolower; +/** + * @internal + */ class ExpressionResolver { public static function getUnresolvedClassConstExpr( diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php index 6e88891a20a..05fcc2931c2 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php @@ -32,6 +32,9 @@ use const DIRECTORY_SEPARATOR; +/** + * @internal + */ class ExpressionScanner { public static function scan( diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php index f703d7da82d..07c3f4ebb71 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php @@ -29,6 +29,9 @@ use function substr_count; use function trim; +/** + * @internal + */ class FunctionLikeDocblockParser { /** diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php index acab93a80f4..9eee2bf5ecb 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php @@ -54,6 +54,9 @@ use function substr; use function trim; +/** + * @internal + */ class FunctionLikeDocblockScanner { /** diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index 14a366da592..9d4c218cbda 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -64,6 +64,9 @@ use function strpos; use function strtolower; +/** + * @internal + */ class FunctionLikeNodeScanner { /** diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php b/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php index 6b992cedd82..e594ac9afb1 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php @@ -16,6 +16,9 @@ use function implode; use function strtolower; +/** + * @internal + */ class TypeHintResolver { /** diff --git a/src/Psalm/Internal/PhpVisitor/TraitFinder.php b/src/Psalm/Internal/PhpVisitor/TraitFinder.php index 93a05225c15..55d177e351d 100644 --- a/src/Psalm/Internal/PhpVisitor/TraitFinder.php +++ b/src/Psalm/Internal/PhpVisitor/TraitFinder.php @@ -14,6 +14,8 @@ /** * Given a list of file diffs, this scans an AST to find the sections it can replace, and parses * just those methods. + * + * @internal */ class TraitFinder extends PhpParser\NodeVisitorAbstract { diff --git a/src/Psalm/Internal/PhpVisitor/TypeMappingVisitor.php b/src/Psalm/Internal/PhpVisitor/TypeMappingVisitor.php index 05704e142ad..da987822342 100644 --- a/src/Psalm/Internal/PhpVisitor/TypeMappingVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/TypeMappingVisitor.php @@ -8,6 +8,9 @@ use PhpParser\NodeVisitorAbstract; use Psalm\Internal\Provider\NodeDataProvider; +/** + * @internal + */ class TypeMappingVisitor extends NodeVisitorAbstract { private $fake_type_provider; diff --git a/src/Psalm/Internal/PluginManager/ComposerLock.php b/src/Psalm/Internal/PluginManager/ComposerLock.php index fd5d251e9db..7511d14c3c1 100644 --- a/src/Psalm/Internal/PluginManager/ComposerLock.php +++ b/src/Psalm/Internal/PluginManager/ComposerLock.php @@ -12,6 +12,9 @@ use function json_last_error; use function json_last_error_msg; +/** + * @internal + */ class ComposerLock { /** @var string[] */ diff --git a/src/Psalm/Internal/PluginManager/ConfigFile.php b/src/Psalm/Internal/PluginManager/ConfigFile.php index daab9d8a320..501f48521cd 100644 --- a/src/Psalm/Internal/PluginManager/ConfigFile.php +++ b/src/Psalm/Internal/PluginManager/ConfigFile.php @@ -12,6 +12,9 @@ use function strpos; use function substr; +/** + * @internal + */ class ConfigFile { /** @var string */ diff --git a/src/Psalm/Internal/PluginManager/PluginList.php b/src/Psalm/Internal/PluginManager/PluginList.php index 7f5d90526e7..ddfdb248e8b 100644 --- a/src/Psalm/Internal/PluginManager/PluginList.php +++ b/src/Psalm/Internal/PluginManager/PluginList.php @@ -11,6 +11,9 @@ use function array_search; use function strpos; +/** + * @internal + */ class PluginList { /** @var null|ConfigFile */ diff --git a/src/Psalm/Internal/PluginManager/PluginListFactory.php b/src/Psalm/Internal/PluginManager/PluginListFactory.php index 0683365f72e..727362fe46f 100644 --- a/src/Psalm/Internal/PluginManager/PluginListFactory.php +++ b/src/Psalm/Internal/PluginManager/PluginListFactory.php @@ -12,6 +12,9 @@ use const DIRECTORY_SEPARATOR; +/** + * @internal + */ class PluginListFactory { /** @var string */ diff --git a/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php b/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php index d3328a5d128..19606c9aa40 100644 --- a/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php +++ b/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php @@ -13,6 +13,9 @@ use const ENT_QUOTES; +/** + * @internal + */ class HtmlFunctionTainter implements AddTaintsInterface, RemoveTaintsInterface { /** diff --git a/src/Psalm/Internal/Provider/FakeFileProvider.php b/src/Psalm/Internal/Provider/FakeFileProvider.php index 5d506bdeb2d..cdb513c6618 100644 --- a/src/Psalm/Internal/Provider/FakeFileProvider.php +++ b/src/Psalm/Internal/Provider/FakeFileProvider.php @@ -6,6 +6,9 @@ use function strpos; use function strtolower; +/** + * @internal + */ class FakeFileProvider extends FileProvider { /** diff --git a/src/Psalm/Internal/Provider/FileProvider.php b/src/Psalm/Internal/Provider/FileProvider.php index 6d85e1458e9..1296574b274 100644 --- a/src/Psalm/Internal/Provider/FileProvider.php +++ b/src/Psalm/Internal/Provider/FileProvider.php @@ -19,6 +19,9 @@ use const DIRECTORY_SEPARATOR; +/** + * @internal + */ class FileProvider { /** diff --git a/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php b/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php index 1d80b56f78e..3ac39b2bca6 100644 --- a/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php +++ b/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php @@ -18,10 +18,12 @@ use const DIRECTORY_SEPARATOR; /** - * @psalm-import-type FileMapType from Analyzer + * @psalm-import-type FileMapType from Analyzer * * Used to determine which files reference other files, necessary for using the --diff * option from the command line. + * + * @internal */ class FileReferenceCacheProvider { diff --git a/src/Psalm/Internal/Provider/FileReferenceProvider.php b/src/Psalm/Internal/Provider/FileReferenceProvider.php index 431726cae6a..9419bfdcab4 100644 --- a/src/Psalm/Internal/Provider/FileReferenceProvider.php +++ b/src/Psalm/Internal/Provider/FileReferenceProvider.php @@ -21,6 +21,8 @@ * * Used to determine which files reference other files, necessary for using the --diff * option from the command line. + * + * @internal */ class FileReferenceProvider { diff --git a/src/Psalm/Internal/Provider/FunctionExistenceProvider.php b/src/Psalm/Internal/Provider/FunctionExistenceProvider.php index 57c4a8c4f1f..643f56b0231 100644 --- a/src/Psalm/Internal/Provider/FunctionExistenceProvider.php +++ b/src/Psalm/Internal/Provider/FunctionExistenceProvider.php @@ -11,6 +11,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class FunctionExistenceProvider { /** diff --git a/src/Psalm/Internal/Provider/FunctionParamsProvider.php b/src/Psalm/Internal/Provider/FunctionParamsProvider.php index d6b70c4b9d8..df82e60d9fa 100644 --- a/src/Psalm/Internal/Provider/FunctionParamsProvider.php +++ b/src/Psalm/Internal/Provider/FunctionParamsProvider.php @@ -15,6 +15,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class FunctionParamsProvider { /** diff --git a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php index 8b124ea5532..8e03671778a 100644 --- a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php @@ -48,6 +48,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class FunctionReturnTypeProvider { /** diff --git a/src/Psalm/Internal/Provider/MethodExistenceProvider.php b/src/Psalm/Internal/Provider/MethodExistenceProvider.php index 0f637728ca6..695f22e2dca 100644 --- a/src/Psalm/Internal/Provider/MethodExistenceProvider.php +++ b/src/Psalm/Internal/Provider/MethodExistenceProvider.php @@ -12,6 +12,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class MethodExistenceProvider { /** diff --git a/src/Psalm/Internal/Provider/MethodParamsProvider.php b/src/Psalm/Internal/Provider/MethodParamsProvider.php index 7cf39e78f75..b8a44c7a906 100644 --- a/src/Psalm/Internal/Provider/MethodParamsProvider.php +++ b/src/Psalm/Internal/Provider/MethodParamsProvider.php @@ -16,6 +16,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class MethodParamsProvider { /** diff --git a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php index f29dc77d6cc..08fe1f8c0c9 100644 --- a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php @@ -20,6 +20,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class MethodReturnTypeProvider { /** diff --git a/src/Psalm/Internal/Provider/MethodVisibilityProvider.php b/src/Psalm/Internal/Provider/MethodVisibilityProvider.php index e9a876bc638..6942c24e169 100644 --- a/src/Psalm/Internal/Provider/MethodVisibilityProvider.php +++ b/src/Psalm/Internal/Provider/MethodVisibilityProvider.php @@ -13,6 +13,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class MethodVisibilityProvider { /** diff --git a/src/Psalm/Internal/Provider/NodeDataProvider.php b/src/Psalm/Internal/Provider/NodeDataProvider.php index 3987a231ffd..c642d3788f4 100644 --- a/src/Psalm/Internal/Provider/NodeDataProvider.php +++ b/src/Psalm/Internal/Provider/NodeDataProvider.php @@ -16,6 +16,9 @@ use Psalm\Type\Union; use SplObjectStorage; +/** + * @internal + */ class NodeDataProvider implements NodeTypeProvider { /** @var SplObjectStorage */ diff --git a/src/Psalm/Internal/Provider/ProjectCacheProvider.php b/src/Psalm/Internal/Provider/ProjectCacheProvider.php index 886c467bc8f..3e41c5b2371 100644 --- a/src/Psalm/Internal/Provider/ProjectCacheProvider.php +++ b/src/Psalm/Internal/Provider/ProjectCacheProvider.php @@ -17,6 +17,8 @@ /** * Used to determine which files reference other files, necessary for using the --diff * option from the command line. + * + * @internal */ class ProjectCacheProvider { diff --git a/src/Psalm/Internal/Provider/PropertyExistenceProvider.php b/src/Psalm/Internal/Provider/PropertyExistenceProvider.php index 6af68fef25e..93e23b2de8b 100644 --- a/src/Psalm/Internal/Provider/PropertyExistenceProvider.php +++ b/src/Psalm/Internal/Provider/PropertyExistenceProvider.php @@ -13,6 +13,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class PropertyExistenceProvider { /** diff --git a/src/Psalm/Internal/Provider/PropertyTypeProvider.php b/src/Psalm/Internal/Provider/PropertyTypeProvider.php index 2e3d1dbee00..788f839a183 100644 --- a/src/Psalm/Internal/Provider/PropertyTypeProvider.php +++ b/src/Psalm/Internal/Provider/PropertyTypeProvider.php @@ -14,6 +14,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class PropertyTypeProvider { /** diff --git a/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php b/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php index 2b127672ac0..eb2f6652dcd 100644 --- a/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php +++ b/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php @@ -12,6 +12,9 @@ use function strtolower; +/** + * @internal + */ class DomDocumentPropertyTypeProvider implements PropertyTypeProviderInterface { public static function getPropertyType(PropertyTypeProviderEvent $event): ?Union diff --git a/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php b/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php index 4ad57941662..0d63450b4d3 100644 --- a/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php +++ b/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php @@ -13,6 +13,9 @@ use function is_subclass_of; use function strtolower; +/** + * @internal + */ class PropertyVisibilityProvider { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayChunkReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayChunkReturnTypeProvider.php index 86877094546..1a1fc376f99 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayChunkReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayChunkReturnTypeProvider.php @@ -15,6 +15,9 @@ use function count; +/** + * @internal + */ class ArrayChunkReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php index 36e15cb9bd8..1225be7458c 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php @@ -16,6 +16,9 @@ use function count; use function reset; +/** + * @internal + */ class ArrayColumnReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php index 9a16f980eee..03dc378d46a 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php @@ -13,6 +13,9 @@ use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Union; +/** + * @internal + */ class ArrayFillReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php index fa11cf9f74d..d0ef928c440 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php @@ -33,6 +33,9 @@ use function reset; use function spl_object_id; +/** + * @internal + */ class ArrayFilterReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php index 47c786e2802..2d64e4b8757 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php @@ -44,6 +44,9 @@ use function strpos; use function substr; +/** + * @internal + */ class ArrayMapReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index 1e038602bc9..c58b44d512d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -24,6 +24,9 @@ use function is_string; use function max; +/** + * @internal + */ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php index f5859c174c5..4ad56bdd452 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php @@ -16,6 +16,9 @@ use function count; +/** + * @internal + */ class ArrayPadReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php index d0563dbaaca..5ac91d639da 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php @@ -20,6 +20,9 @@ use function array_merge; use function array_shift; +/** + * @internal + */ class ArrayPointerAdjustmentReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php index 594918e2b37..0ea580949d7 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php @@ -14,6 +14,9 @@ use Psalm\Type\Atomic\TNull; use Psalm\Type\Union; +/** + * @internal + */ class ArrayPopReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php index 8c7062b31aa..41830ca965d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php @@ -12,6 +12,9 @@ use Psalm\Type\Atomic\TList; use Psalm\Type\Union; +/** + * @internal + */ class ArrayRandReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php index a9d27ba8774..c8958f982b3 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php @@ -27,6 +27,9 @@ use function strtolower; use function substr; +/** + * @internal + */ class ArrayReduceReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php index 0fab0664810..4c2d2705df3 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php @@ -11,6 +11,9 @@ use Psalm\Type\Atomic\TList; use Psalm\Type\Union; +/** + * @internal + */ class ArrayReverseReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php index 399bb259319..e0adab140c0 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php @@ -16,6 +16,9 @@ use function array_merge; use function array_shift; +/** + * @internal + */ class ArraySliceReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php index 6f4d0625da1..125c42ee2c9 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php @@ -11,6 +11,9 @@ use Psalm\Type\Atomic\TList; use Psalm\Type\Union; +/** + * @internal + */ class ArraySpliceReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php index 5499ad275d2..eb6ef09c806 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php @@ -13,6 +13,9 @@ use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Union; +/** + * @internal + */ class ArrayUniqueReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php index 0544510f8c6..3ec26808007 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php @@ -18,6 +18,9 @@ use function array_merge; use function array_shift; +/** + * @internal + */ class ArrayValuesReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php index a94e5817a43..ef2fe97d0c8 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php @@ -11,6 +11,9 @@ use Psalm\Type\Atomic\TClosure; use Psalm\Type\Union; +/** + * @internal + */ class ClosureFromCallableReturnTypeProvider implements MethodReturnTypeProviderInterface { public static function getClassLikeNames(): array diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php index 6d7723bfb0d..c08a570762d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php @@ -8,6 +8,9 @@ use Psalm\Type; use Psalm\Type\Union; +/** + * @internal + */ class DomNodeAppendChild implements MethodReturnTypeProviderInterface { public static function getClassLikeNames(): array diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php index 81001ff95fa..af6beb4c8a7 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php @@ -17,6 +17,9 @@ use function count; +/** + * @internal + */ class ExplodeReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php index 0e267608a7a..f35fdc28293 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php @@ -27,6 +27,9 @@ use const FILTER_VALIDATE_REGEXP; use const FILTER_VALIDATE_URL; +/** + * @internal + */ class FilterVarReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php index 4f99fdddb2d..00f633bd858 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php @@ -9,6 +9,9 @@ use Psalm\Type\Atomic\TNull; use Psalm\Type\Union; +/** + * @internal + */ class FirstArgStringReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php index 4c7d78655a4..a5f07266a36 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php @@ -8,6 +8,9 @@ use Psalm\Type; use Psalm\Type\Union; +/** + * @internal + */ class GetClassMethodsReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php index 888280cd346..20c43ab9251 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php @@ -19,6 +19,9 @@ use function reset; use function strtolower; +/** + * @internal + */ class GetObjectVarsReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php index b6c6078813f..0cbd821ab00 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php @@ -8,6 +8,9 @@ use Psalm\Type; use Psalm\Type\Union; +/** + * @internal + */ class HexdecReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php index c97048aefd1..6884757d358 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php @@ -14,6 +14,9 @@ use function assert; use function in_array; +/** + * @internal + */ class ImagickPixelColorReturnTypeProvider implements MethodReturnTypeProviderInterface { public static function getClassLikeNames(): array diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php index 519984f2f52..4c4a61c6e65 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php @@ -11,6 +11,9 @@ use Psalm\Type\Atomic\TList; use Psalm\Type\Union; +/** + * @internal + */ class InArrayReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php index 52d26475169..a5176b1a1ef 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php @@ -20,6 +20,9 @@ use function array_shift; use function assert; +/** + * @internal + */ class IteratorToArrayReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php index 1a24e22b30b..b6279c0d35b 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php @@ -23,6 +23,9 @@ use function max; use function min; +/** + * @internal + */ class MinMaxReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php index 4b9ea038957..a8d37a92465 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php @@ -10,6 +10,9 @@ use Psalm\Type\Atomic\TInt; use Psalm\Type\Union; +/** + * @internal + */ class MktimeReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php index 8beea77c32d..de5fdd5b029 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php @@ -26,6 +26,9 @@ use const PHP_URL_SCHEME; use const PHP_URL_USER; +/** + * @internal + */ class ParseUrlReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php index 86ee127178a..1d2521ea003 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php @@ -17,6 +17,9 @@ use function class_exists; +/** + * @internal + */ class PdoStatementReturnTypeProvider implements MethodReturnTypeProviderInterface { public static function getClassLikeNames(): array diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php index e352ffd48e8..cd88b142770 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php @@ -10,6 +10,9 @@ use Psalm\Storage\FunctionLikeParameter; use Psalm\Type; +/** + * @internal + */ class PdoStatementSetFetchMode implements MethodParamsProviderInterface { public static function getClassLikeNames(): array diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/RandReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/RandReturnTypeProvider.php index 32530db9bef..7d72ceb941f 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/RandReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/RandReturnTypeProvider.php @@ -14,6 +14,9 @@ use function count; +/** + * @internal + */ class RandReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php index 88e71c3840d..612ebdb845a 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php @@ -9,6 +9,9 @@ use function count; +/** + * @internal + */ class SimpleXmlElementAsXml implements MethodReturnTypeProviderInterface { public static function getClassLikeNames(): array diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php index d32da31e979..6061ddf0d34 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php @@ -12,6 +12,9 @@ use function count; use function in_array; +/** + * @internal + */ class StrReplaceReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php index cb3b01958d8..6e73cff96d3 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php @@ -12,6 +12,9 @@ use function in_array; +/** + * @internal + */ class StrTrReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php index 385c6b61b5d..d8da5f1766b 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php @@ -21,6 +21,9 @@ use const E_USER_NOTICE; use const E_USER_WARNING; +/** + * @internal + */ class TriggerErrorReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php index cab2b99971f..82151bd1369 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php @@ -15,6 +15,9 @@ use function count; +/** + * @internal + */ class VersionCompareReturnTypeProvider implements FunctionReturnTypeProviderInterface { /** diff --git a/src/Psalm/Internal/RuntimeCaches.php b/src/Psalm/Internal/RuntimeCaches.php index b34fa2c3470..694b49e41c1 100644 --- a/src/Psalm/Internal/RuntimeCaches.php +++ b/src/Psalm/Internal/RuntimeCaches.php @@ -18,6 +18,9 @@ use Psalm\Internal\Type\TypeTokenizer; use Psalm\IssueBuffer; +/** + * @internal + */ abstract class RuntimeCaches { public static function clearAll(): void diff --git a/src/Psalm/Internal/Scanner/DocblockParser.php b/src/Psalm/Internal/Scanner/DocblockParser.php index 416f487e5a2..7c854b7c57f 100644 --- a/src/Psalm/Internal/Scanner/DocblockParser.php +++ b/src/Psalm/Internal/Scanner/DocblockParser.php @@ -19,6 +19,8 @@ /** * This class will parse Docblocks in order to extract known tags from them + * + * @internal */ class DocblockParser { diff --git a/src/Psalm/Internal/Scanner/ParsedDocblock.php b/src/Psalm/Internal/Scanner/ParsedDocblock.php index 26c5b01363c..8f5b87192ec 100644 --- a/src/Psalm/Internal/Scanner/ParsedDocblock.php +++ b/src/Psalm/Internal/Scanner/ParsedDocblock.php @@ -5,6 +5,9 @@ use function explode; use function trim; +/** + * @internal + */ class ParsedDocblock { /** @var string */ diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayOffsetFetch.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayOffsetFetch.php index fde629653e6..bdb2c16f51e 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayOffsetFetch.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayOffsetFetch.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class ArrayOffsetFetch extends UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/ArraySpread.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/ArraySpread.php index 1a1ec7bdf19..9bd4d069a61 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/ArraySpread.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/ArraySpread.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class ArraySpread extends UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayValue.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayValue.php index e42888f29b6..94a246ac31d 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayValue.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayValue.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class ArrayValue extends UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/ClassConstant.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/ClassConstant.php index 7f789b9ff43..041f02fa2df 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/ClassConstant.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/ClassConstant.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class ClassConstant extends UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/Constant.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/Constant.php index a222fb038e0..5e7d2950872 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/Constant.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/Constant.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class Constant extends UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/KeyValuePair.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/KeyValuePair.php index 7ba4d8d3243..e75c68dae22 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/KeyValuePair.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/KeyValuePair.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class KeyValuePair extends UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/ScalarValue.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/ScalarValue.php index 6fa9727ab2e..757bcdcef0d 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/ScalarValue.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/ScalarValue.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class ScalarValue extends UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedAdditionOp.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedAdditionOp.php index 90c2f9e9c20..565f76a9309 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedAdditionOp.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedAdditionOp.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class UnresolvedAdditionOp extends UnresolvedBinaryOp { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBinaryOp.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBinaryOp.php index 3662cc6ed84..adaa1b10ba0 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBinaryOp.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBinaryOp.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ abstract class UnresolvedBinaryOp extends UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseAnd.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseAnd.php index c4fb77a3553..2b59f99a33a 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseAnd.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseAnd.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class UnresolvedBitwiseAnd extends UnresolvedBinaryOp { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseOr.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseOr.php index 751fc5eebb4..e6f2c08a626 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseOr.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseOr.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class UnresolvedBitwiseOr extends UnresolvedBinaryOp { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseXor.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseXor.php index cf72dacd6a9..c9ff455bfda 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseXor.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseXor.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class UnresolvedBitwiseXor extends UnresolvedBinaryOp { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedConcatOp.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedConcatOp.php index 72c871811c2..29899ab3b8f 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedConcatOp.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedConcatOp.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class UnresolvedConcatOp extends UnresolvedBinaryOp { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedDivisionOp.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedDivisionOp.php index b990265cf95..9efa28eb7c4 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedDivisionOp.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedDivisionOp.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class UnresolvedDivisionOp extends UnresolvedBinaryOp { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedMultiplicationOp.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedMultiplicationOp.php index 76905bd6c5c..d2572f9344a 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedMultiplicationOp.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedMultiplicationOp.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class UnresolvedMultiplicationOp extends UnresolvedBinaryOp { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedSubtractionOp.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedSubtractionOp.php index 76a06cc6a91..87ceb46c9c0 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedSubtractionOp.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedSubtractionOp.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ class UnresolvedSubtractionOp extends UnresolvedBinaryOp { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedTernary.php b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedTernary.php index f9618f0ffa4..ede42e27232 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedTernary.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedTernary.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class UnresolvedTernary extends UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Scanner/UnresolvedConstantComponent.php b/src/Psalm/Internal/Scanner/UnresolvedConstantComponent.php index 5f96f7d1ac6..5d7bf494b03 100644 --- a/src/Psalm/Internal/Scanner/UnresolvedConstantComponent.php +++ b/src/Psalm/Internal/Scanner/UnresolvedConstantComponent.php @@ -4,6 +4,8 @@ /** * @psalm-immutable + * + * @internal */ abstract class UnresolvedConstantComponent { diff --git a/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php b/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php index 6bc81302b96..d6efd715654 100644 --- a/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php @@ -25,6 +25,9 @@ use function array_slice; use function rtrim; +/** + * @internal + */ class ClassLikeStubGenerator { /** diff --git a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php index 224b0e5a523..c9888361380 100644 --- a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php @@ -107,6 +107,9 @@ use function rtrim; use function strpos; +/** + * @internal + */ class StubsGenerator { public static function getAll( diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index e55661873aa..ec6c8ade0a1 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -50,6 +50,9 @@ use function strpos; use function substr; +/** + * @internal + */ class AssertionReconciler extends Reconciler { /** diff --git a/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php b/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php index bdfafc95626..05bdb46c9f8 100644 --- a/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php +++ b/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php @@ -5,6 +5,9 @@ use Psalm\Type\Atomic; use Psalm\Type\Union; +/** + * @internal + */ class TypeComparisonResult { /** diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index ef19ad510d3..eb763bba427 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -29,6 +29,9 @@ use function strtolower; use function substr; +/** + * @internal + */ class NegatedAssertionReconciler extends Reconciler { /** diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index c943f623581..a23020d5116 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -67,6 +67,8 @@ * This class receives a known type and an assertion (probably coming from AssertionFinder). The goal is to refine * the known type using the assertion. For example: old type is `int` assertion is `>5` result is `int<6, max>`. * Complex reconciliation takes part in AssertionReconciler if this class couldn't handle the reconciliation + * + * @internal */ class SimpleAssertionReconciler extends Reconciler { diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index f86dfeaf466..87eb7fc8834 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -55,6 +55,9 @@ use function strpos; use function substr; +/** + * @internal + */ class SimpleNegatedAssertionReconciler extends Reconciler { /** diff --git a/src/Psalm/Internal/Type/TemplateBound.php b/src/Psalm/Internal/Type/TemplateBound.php index da8da461acf..f454200589d 100644 --- a/src/Psalm/Internal/Type/TemplateBound.php +++ b/src/Psalm/Internal/Type/TemplateBound.php @@ -4,6 +4,9 @@ use Psalm\Type\Union; +/** + * @internal + */ class TemplateBound { /** diff --git a/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php b/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php index d0aff71b2f6..38e542d926e 100644 --- a/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php +++ b/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php @@ -30,6 +30,9 @@ use function array_values; use function strpos; +/** + * @internal + */ class TemplateInferredTypeReplacer { /** diff --git a/src/Psalm/Internal/Type/TemplateResult.php b/src/Psalm/Internal/Type/TemplateResult.php index c213eb59c08..c92e4af20c9 100644 --- a/src/Psalm/Internal/Type/TemplateResult.php +++ b/src/Psalm/Internal/Type/TemplateResult.php @@ -20,6 +20,8 @@ * parameters — given a parameter type `callable(): T2` and an argument typed as * `callable(): string`, `string` will be added as a _lower_ bound for the template * param `T2`. + * + * @internal */ class TemplateResult { diff --git a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php index af4ad25914a..45cf799be45 100644 --- a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php +++ b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php @@ -45,6 +45,9 @@ use function substr; use function usort; +/** + * @internal + */ class TemplateStandinTypeReplacer { /** diff --git a/src/Psalm/Internal/Type/TypeAlias/ClassTypeAlias.php b/src/Psalm/Internal/Type/TypeAlias/ClassTypeAlias.php index 5f5146e98a9..6b7842ebc16 100644 --- a/src/Psalm/Internal/Type/TypeAlias/ClassTypeAlias.php +++ b/src/Psalm/Internal/Type/TypeAlias/ClassTypeAlias.php @@ -5,6 +5,9 @@ use Psalm\Internal\Type\TypeAlias; use Psalm\Type\Atomic; +/** + * @internal + */ class ClassTypeAlias implements TypeAlias { /** diff --git a/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php b/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php index 9ed473c6f3c..1e1f99fe320 100644 --- a/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php +++ b/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class InlineTypeAlias implements TypeAlias { diff --git a/src/Psalm/Internal/Type/TypeAlias/LinkableTypeAlias.php b/src/Psalm/Internal/Type/TypeAlias/LinkableTypeAlias.php index a0c309dd556..bdd13819e6c 100644 --- a/src/Psalm/Internal/Type/TypeAlias/LinkableTypeAlias.php +++ b/src/Psalm/Internal/Type/TypeAlias/LinkableTypeAlias.php @@ -6,6 +6,8 @@ /** * @psalm-immutable + * + * @internal */ class LinkableTypeAlias implements TypeAlias { diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index b40877b909d..111a28561bd 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -70,6 +70,9 @@ use function strtolower; use function substr; +/** + * @internal + */ class TypeCombiner { /** diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index cfd0b12908e..26e2658ca4b 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -92,6 +92,9 @@ use function strtolower; use function substr; +/** + * @internal + */ class TypeParser { /** diff --git a/src/Psalm/Internal/Type/TypeTokenizer.php b/src/Psalm/Internal/Type/TypeTokenizer.php index 996eb2d41e3..8230990b978 100644 --- a/src/Psalm/Internal/Type/TypeTokenizer.php +++ b/src/Psalm/Internal/Type/TypeTokenizer.php @@ -19,6 +19,9 @@ use function strpos; use function strtolower; +/** + * @internal + */ class TypeTokenizer { /** diff --git a/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php b/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php index 94d55b17ac7..fb57c5c2059 100644 --- a/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php +++ b/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php @@ -10,6 +10,9 @@ use function strtolower; +/** + * @internal + */ class ContainsClassLikeVisitor extends NodeVisitor { /** diff --git a/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php b/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php index 7c619489711..8fb36b79c2b 100644 --- a/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php +++ b/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php @@ -11,6 +11,9 @@ use Psalm\Type\NodeVisitor; use Psalm\Type\TypeNode; +/** + * @internal + */ class ContainsLiteralVisitor extends NodeVisitor { /** diff --git a/src/Psalm/Internal/TypeVisitor/FromDocblockSetter.php b/src/Psalm/Internal/TypeVisitor/FromDocblockSetter.php index 3846e766119..2f4a7b7cff0 100644 --- a/src/Psalm/Internal/TypeVisitor/FromDocblockSetter.php +++ b/src/Psalm/Internal/TypeVisitor/FromDocblockSetter.php @@ -8,6 +8,9 @@ use Psalm\Type\TypeNode; use Psalm\Type\Union; +/** + * @internal + */ class FromDocblockSetter extends NodeVisitor { /** diff --git a/src/Psalm/Internal/TypeVisitor/TemplateTypeCollector.php b/src/Psalm/Internal/TypeVisitor/TemplateTypeCollector.php index 1b3d365d69c..5806638342d 100644 --- a/src/Psalm/Internal/TypeVisitor/TemplateTypeCollector.php +++ b/src/Psalm/Internal/TypeVisitor/TemplateTypeCollector.php @@ -10,6 +10,9 @@ use Psalm\Type\TypeNode; use Psalm\Type\Union; +/** + * @internal + */ class TemplateTypeCollector extends NodeVisitor { /** diff --git a/src/Psalm/Internal/TypeVisitor/TypeChecker.php b/src/Psalm/Internal/TypeVisitor/TypeChecker.php index 7cb18b13d56..fbf9906bbac 100644 --- a/src/Psalm/Internal/TypeVisitor/TypeChecker.php +++ b/src/Psalm/Internal/TypeVisitor/TypeChecker.php @@ -39,6 +39,9 @@ use function strpos; use function strtolower; +/** + * @internal + */ class TypeChecker extends NodeVisitor { /** diff --git a/src/Psalm/Internal/TypeVisitor/TypeScanner.php b/src/Psalm/Internal/TypeVisitor/TypeScanner.php index 3e742886991..48f6796ef28 100644 --- a/src/Psalm/Internal/TypeVisitor/TypeScanner.php +++ b/src/Psalm/Internal/TypeVisitor/TypeScanner.php @@ -12,6 +12,9 @@ use function strtolower; +/** + * @internal + */ class TypeScanner extends NodeVisitor { private $scanner; From 2066a21ca8f80bd44d537e5289696c16cdbd3baf Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 09:18:55 +0200 Subject: [PATCH 24/78] Documented addition of `@internal` annotations --- UPGRADING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UPGRADING.md b/UPGRADING.md index b18df6329be..2ee7fbbc47d 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -114,6 +114,8 @@ - `Psalm\Type\Atomic\TValueOfClassConstant` - `Psalm\Type\Atomic\TVoid` - `Psalm\Type\Union` + - While not a BC break per se, all classes / interfaces / traits / enums under + `Psalm\Internal` namespace are now marked `@internal`. ## Removed - [BC] Property `Psalm\Codebase::$php_major_version` was removed, use From 706f2a77618f11bde61ca8349dffa54e625f3226 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 12:04:35 +0200 Subject: [PATCH 25/78] Drop legacy procedural files --- composer.json | 1 - psalm | 2 + psalm-baseline.xml | 25 ---------- psalm-language-server | 2 + psalm-plugin | 2 + psalm-refactor | 2 + psalm.xml.dist | 5 ++ psalter | 2 + src/command_functions.php | 88 ----------------------------------- src/functions.php | 13 ------ src/psalm-language-server.php | 10 ---- src/psalm-refactor.php | 10 ---- src/psalm.php | 10 ---- src/psalm_plugin.php | 10 ---- src/psalter.php | 10 ---- 15 files changed, 15 insertions(+), 177 deletions(-) delete mode 100644 src/command_functions.php delete mode 100644 src/functions.php delete mode 100644 src/psalm-language-server.php delete mode 100644 src/psalm-refactor.php delete mode 100644 src/psalm.php delete mode 100644 src/psalm_plugin.php delete mode 100644 src/psalter.php diff --git a/composer.json b/composer.json index 94bb0c5eff9..67e1687a34e 100644 --- a/composer.json +++ b/composer.json @@ -82,7 +82,6 @@ "Psalm\\": "src/Psalm/" }, "files": [ - "src/functions.php", "src/spl_object_id.php" ] }, diff --git a/psalm b/psalm index a633cb872ac..9d05b6082a4 100755 --- a/psalm +++ b/psalm @@ -1,6 +1,8 @@ #!/usr/bin/env php $type[0] - - - LanguageServer::run($argv) - - - - - Refactor::run($argv) - - - - - Psalm::run($argv) - - - - - Plugin::run() - - - - - Psalter::run($argv) - - $subNodes['expr'] diff --git a/psalm-language-server b/psalm-language-server index a1869f329ab..4bc838c8a1a 100755 --- a/psalm-language-server +++ b/psalm-language-server @@ -1,6 +1,8 @@ #!/usr/bin/env php + + + + + diff --git a/psalter b/psalter index c36c93fec99..8fb9dd3e648 100755 --- a/psalter +++ b/psalter @@ -1,6 +1,8 @@ #!/usr/bin/env php - * @deprecated going to be removed in Psalm 5 - */ -function getArguments(): array -{ - return CliUtils::getArguments(); -} - -/** - * @param string|array|null|false $f_paths - * - * @return list|null - * @deprecated going to be removed in Psalm 5 - */ -function getPathsToCheck($f_paths): ?array -{ - return CliUtils::getPathsToCheck($f_paths); -} - -/** - * @psalm-pure - * @deprecated going to be removed in Psalm 5 - */ -function getPsalmHelpText(): string -{ - return CliUtils::getPsalmHelpText(); -} - -/** @deprecated going to be removed in Psalm 5 */ -function initialiseConfig( - ?string $path_to_config, - string $current_dir, - string $output_format, - ?ClassLoader $first_autoloader, - bool $create_if_non_existent = false -): Config { - return CliUtils::initializeConfig( - $path_to_config, - $current_dir, - $output_format, - $first_autoloader, - $create_if_non_existent - ); -} - -/** @deprecated going to be removed in Psalm 5 */ -function update_config_file(Config $config, string $config_file_path, string $baseline_path): void -{ - CliUtils::updateConfigFile($config, $config_file_path, $baseline_path); -} - -/** @deprecated going to be removed in Psalm 5 */ -function get_path_to_config(array $options): ?string -{ - return CliUtils::getPathToConfig($options); -} - -/** - * @psalm-pure - * @deprecated going to be removed in Psalm 5 - */ -function getMemoryLimitInBytes(): int -{ - return CliUtils::getMemoryLimitInBytes(); -} diff --git a/src/functions.php b/src/functions.php deleted file mode 100644 index a44e53cc7e1..00000000000 --- a/src/functions.php +++ /dev/null @@ -1,13 +0,0 @@ - Date: Mon, 3 Jan 2022 12:41:17 +0200 Subject: [PATCH 26/78] Dropped removed config entries --- config.xsd | 10 ----- src/Psalm/Config.php | 104 ++++++++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 55 deletions(-) diff --git a/config.xsd b/config.xsd index 95d079cf76c..46840b0a3a9 100644 --- a/config.xsd +++ b/config.xsd @@ -32,16 +32,6 @@ - - - - - - Deprecated. Has no effect since Psalm 3 and is going to be removed in Psalm 5. - - - - diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index f7e35324f92..ad27a3e237a 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -5,6 +5,7 @@ use Composer\Autoload\ClassLoader; use Composer\Semver\Constraint\Constraint; use Composer\Semver\VersionParser; +use DOMAttr; use DOMDocument; use DomElement; use InvalidArgumentException; @@ -762,6 +763,58 @@ private static function lineNumberToByteOffset(string $string, int $line_number) return $offset; } + private static function processDeprecatedAttribute( + DOMAttr $attribute, + string $file_contents, + self $config, + string $config_path + ): void { + $line = $attribute->getLineNo(); + assert($line > 0); // getLineNo() always returns non-zero for nodes loaded from file + + $offset = self::lineNumberToByteOffset($file_contents, $line); + $attribute_start = strrpos($file_contents, $attribute->name, $offset - strlen($file_contents)) ?: 0; + $attribute_end = $attribute_start + strlen($attribute->name) - 1; + + $config->config_issues[] = new ConfigIssue( + 'Attribute "' . $attribute->name . '" is deprecated ' + . 'and is going to be removed in the next major version', + new Raw( + $file_contents, + $config_path, + basename($config_path), + $attribute_start, + $attribute_end + ) + ); + } + + private static function processDeprecatedElement( + DomElement $deprecated_element_xml, + string $file_contents, + self $config, + string $config_path + ): void { + $line = $deprecated_element_xml->getLineNo(); + assert($line > 0); + + $offset = self::lineNumberToByteOffset($file_contents, $line); + $element_start = strpos($file_contents, $deprecated_element_xml->localName, $offset) ?: 0; + $element_end = $element_start + strlen($deprecated_element_xml->localName) - 1; + + $config->config_issues[] = new ConfigIssue( + 'Element "' . $deprecated_element_xml->localName . '" is deprecated ' + . 'and is going to be removed in the next major version', + new Raw( + $file_contents, + $config_path, + basename($config_path), + $element_start, + $element_end + ) + ); + } + private static function processConfigDeprecations( self $config, DOMDocument $dom_document, @@ -770,15 +823,11 @@ private static function processConfigDeprecations( ): void { $config->config_issues = []; - // Attributes to be removed in Psalm 5 - $deprecated_attributes = [ - 'allowCoercionFromStringToClassConst', - 'allowPhpStormGenerics', - ]; + // Attributes to be removed in Psalm 6 + $deprecated_attributes = []; - $deprecated_elements = [ - 'exitFunctions', - ]; + /** @var list */ + $deprecated_elements = []; $psalm_element_item = $dom_document->getElementsByTagName('psalm')->item(0); assert($psalm_element_item !== null); @@ -786,24 +835,7 @@ private static function processConfigDeprecations( foreach ($attributes as $attribute) { if (in_array($attribute->name, $deprecated_attributes, true)) { - $line = $attribute->getLineNo(); - assert($line > 0); // getLineNo() always returns non-zero for nodes loaded from file - - $offset = self::lineNumberToByteOffset($file_contents, $line); - $attribute_start = strrpos($file_contents, $attribute->name, $offset - strlen($file_contents)) ?: 0; - $attribute_end = $attribute_start + strlen($attribute->name) - 1; - - $config->config_issues[] = new ConfigIssue( - 'Attribute "' . $attribute->name . '" is deprecated ' - . 'and is going to be removed in the next major version', - new Raw( - $file_contents, - $config_path, - basename($config_path), - $attribute_start, - $attribute_end - ) - ); + self::processDeprecatedAttribute($attribute, $file_contents, $config, $config_path); } } @@ -814,25 +846,7 @@ private static function processConfigDeprecations( ); if ($deprecated_elements_xml->length) { $deprecated_element_xml = $deprecated_elements_xml->item(0); - assert($deprecated_element_xml !== null); - $line = $deprecated_element_xml->getLineNo(); - assert($line > 0); - - $offset = self::lineNumberToByteOffset($file_contents, $line); - $element_start = strpos($file_contents, $deprecated_element, $offset) ?: 0; - $element_end = $element_start + strlen($deprecated_element) - 1; - - $config->config_issues[] = new ConfigIssue( - 'Element "' . $deprecated_element . '" is deprecated ' - . 'and is going to be removed in the next major version', - new Raw( - $file_contents, - $config_path, - basename($config_path), - $element_start, - $element_end - ) - ); + self::processDeprecatedElement($deprecated_element_xml, $file_contents, $config, $config_path); } } } From 2e24a16cbbc5a11ded5d1c8b3190cf73f27bb4a3 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 18:18:44 +0200 Subject: [PATCH 27/78] Dropped legacy plugin API --- psalm.xml.dist | 12 - src/Psalm/Internal/EventDispatcher.php | 264 ++---------------- .../Provider/FunctionExistenceProvider.php | 47 +--- .../Provider/FunctionParamsProvider.php | 64 +---- .../Provider/FunctionReturnTypeProvider.php | 57 +--- .../Provider/MethodExistenceProvider.php | 61 +--- .../Provider/MethodParamsProvider.php | 58 +--- .../Provider/MethodReturnTypeProvider.php | 68 +---- .../Provider/MethodVisibilityProvider.php | 55 +--- .../Provider/PropertyExistenceProvider.php | 58 +--- .../Provider/PropertyTypeProvider.php | 55 +--- .../Provider/PropertyVisibilityProvider.php | 68 +---- src/Psalm/IssueBuffer.php | 4 +- .../Plugin/Hook/AfterAnalysisInterface.php | 23 -- .../Hook/AfterClassLikeAnalysisInterface.php | 29 -- .../AfterClassLikeExistenceCheckInterface.php | 23 -- .../Hook/AfterClassLikeVisitInterface.php | 27 -- .../Hook/AfterCodebasePopulatedInterface.php | 17 -- ...fterEveryFunctionCallAnalysisInterface.php | 20 -- .../Hook/AfterExpressionAnalysisInterface.php | 28 -- .../Hook/AfterFileAnalysisInterface.php | 22 -- .../AfterFunctionCallAnalysisInterface.php | 28 -- .../AfterFunctionLikeAnalysisInterface.php | 28 -- .../Hook/AfterMethodCallAnalysisInterface.php | 32 --- .../Hook/AfterStatementAnalysisInterface.php | 28 -- .../Hook/BeforeFileAnalysisInterface.php | 22 -- .../FunctionExistenceProviderInterface.php | 25 -- .../Hook/FunctionParamsProviderInterface.php | 31 -- .../FunctionReturnTypeProviderInterface.php | 33 --- .../Hook/MethodExistenceProviderInterface.php | 27 -- .../Hook/MethodParamsProviderInterface.php | 32 --- .../MethodReturnTypeProviderInterface.php | 40 --- .../MethodVisibilityProviderInterface.php | 24 -- .../PropertyExistenceProviderInterface.php | 31 -- .../Hook/PropertyTypeProviderInterface.php | 24 -- .../PropertyVisibilityProviderInterface.php | 25 -- .../Hook/StringInterpreterInterface.php | 16 -- src/Psalm/PluginRegistrationSocket.php | 50 +--- tests/Config/PluginTest.php | 89 ------ 39 files changed, 56 insertions(+), 1589 deletions(-) delete mode 100644 src/Psalm/Plugin/Hook/AfterAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterClassLikeAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterClassLikeExistenceCheckInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterClassLikeVisitInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterCodebasePopulatedInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterEveryFunctionCallAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterExpressionAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterFileAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterFunctionCallAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterFunctionLikeAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterMethodCallAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/AfterStatementAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/BeforeFileAnalysisInterface.php delete mode 100644 src/Psalm/Plugin/Hook/FunctionExistenceProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/FunctionParamsProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/FunctionReturnTypeProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/MethodExistenceProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/MethodParamsProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/MethodReturnTypeProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/MethodVisibilityProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/PropertyExistenceProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/PropertyTypeProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/PropertyVisibilityProviderInterface.php delete mode 100644 src/Psalm/Plugin/Hook/StringInterpreterInterface.php diff --git a/psalm.xml.dist b/psalm.xml.dist index bdbaaab6da5..0343b7cb1fc 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -62,17 +62,6 @@ - - - - - - - - - - - @@ -92,7 +81,6 @@ - diff --git a/src/Psalm/Internal/EventDispatcher.php b/src/Psalm/Internal/EventDispatcher.php index 3d0791f6c20..29e8067490d 100644 --- a/src/Psalm/Internal/EventDispatcher.php +++ b/src/Psalm/Internal/EventDispatcher.php @@ -33,20 +33,6 @@ use Psalm\Plugin\EventHandler\Event\StringInterpreterEvent; use Psalm\Plugin\EventHandler\RemoveTaintsInterface; use Psalm\Plugin\EventHandler\StringInterpreterInterface; -use Psalm\Plugin\Hook\AfterAnalysisInterface as LegacyAfterAnalysisInterface; -use Psalm\Plugin\Hook\AfterClassLikeAnalysisInterface as LegacyAfterClassLikeAnalysisInterface; -use Psalm\Plugin\Hook\AfterClassLikeExistenceCheckInterface as LegacyAfterClassLikeExistenceCheckInterface; -use Psalm\Plugin\Hook\AfterClassLikeVisitInterface as LegacyAfterClassLikeVisitInterface; -use Psalm\Plugin\Hook\AfterCodebasePopulatedInterface as LegacyAfterCodebasePopulatedInterface; -use Psalm\Plugin\Hook\AfterEveryFunctionCallAnalysisInterface as LegacyAfterEveryFunctionCallAnalysisInterface; -use Psalm\Plugin\Hook\AfterExpressionAnalysisInterface as LegacyAfterExpressionAnalysisInterface; -use Psalm\Plugin\Hook\AfterFileAnalysisInterface as LegacyAfterFileAnalysisInterface; -use Psalm\Plugin\Hook\AfterFunctionCallAnalysisInterface as LegacyAfterFunctionCallAnalysisInterface; -use Psalm\Plugin\Hook\AfterFunctionLikeAnalysisInterface as LegacyAfterFunctionLikeAnalysisInterface; -use Psalm\Plugin\Hook\AfterMethodCallAnalysisInterface as LegacyAfterMethodCallAnalysisInterface; -use Psalm\Plugin\Hook\AfterStatementAnalysisInterface as LegacyAfterStatementAnalysisInterface; -use Psalm\Plugin\Hook\BeforeFileAnalysisInterface as LegacyBeforeFileAnalysisInterface; -use Psalm\Plugin\Hook\StringInterpreterInterface as LegacyStringInterpreterInterface; use Psalm\Type\Atomic\TLiteralString; use function array_merge; @@ -64,8 +50,6 @@ class EventDispatcher * @var list> */ private $after_method_checks = []; - /** @var list> */ - private $legacy_after_method_checks = []; /** * Static methods to be called after project function checks have completed @@ -77,8 +61,6 @@ class EventDispatcher * @var list> */ public $after_function_checks = []; - /** @var list> */ - public $legacy_after_function_checks = []; /** * Static methods to be called after every function call @@ -90,8 +72,6 @@ class EventDispatcher * @var list> */ public $after_every_function_checks = []; - /** @var list> */ - public $legacy_after_every_function_checks = []; /** * Static methods to be called after expression checks have completed @@ -99,8 +79,6 @@ class EventDispatcher * @var list> */ public $after_expression_checks = []; - /** @var list> */ - public $legacy_after_expression_checks = []; /** * Static methods to be called after statement checks have completed @@ -108,8 +86,6 @@ class EventDispatcher * @var list> */ public $after_statement_checks = []; - /** @var list> */ - public $legacy_after_statement_checks = []; /** * Static methods to be called after method checks have completed @@ -117,8 +93,6 @@ class EventDispatcher * @var list> */ public $string_interpreters = []; - /** @var list> */ - public $legacy_string_interpreters = []; /** * Static methods to be called after classlike exists checks have completed @@ -126,8 +100,6 @@ class EventDispatcher * @var list> */ public $after_classlike_exists_checks = []; - /** @var list> */ - public $legacy_after_classlike_exists_checks = []; /** * Static methods to be called after classlike checks have completed @@ -135,8 +107,6 @@ class EventDispatcher * @var list> */ public $after_classlike_checks = []; - /** @var list> */ - public $legacy_after_classlike_checks = []; /** * Static methods to be called after classlikes have been scanned @@ -144,8 +114,6 @@ class EventDispatcher * @var list> */ private $after_visit_classlikes = []; - /** @var list> */ - private $legacy_after_visit_classlikes = []; /** * Static methods to be called after codebase has been populated @@ -153,8 +121,6 @@ class EventDispatcher * @var list> */ public $after_codebase_populated = []; - /** @var list> */ - public $legacy_after_codebase_populated = []; /** * Static methods to be called after codebase has been populated @@ -162,8 +128,6 @@ class EventDispatcher * @var list> */ public $after_analysis = []; - /** @var list> */ - public $legacy_after_analysis = []; /** * Static methods to be called after a file has been analyzed @@ -171,8 +135,6 @@ class EventDispatcher * @var list> */ public $after_file_checks = []; - /** @var list> */ - public $legacy_after_file_checks = []; /** * Static methods to be called before a file is analyzed @@ -180,8 +142,6 @@ class EventDispatcher * @var list> */ public $before_file_checks = []; - /** @var list> */ - public $legacy_before_file_checks = []; /** * Static methods to be called after functionlike checks have completed @@ -189,8 +149,6 @@ class EventDispatcher * @var list> */ public $after_functionlike_checks = []; - /** @var list> */ - public $legacy_after_functionlike_checks = []; /** * Static methods to be called to see if taints should be added @@ -211,87 +169,59 @@ class EventDispatcher */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyAfterMethodCallAnalysisInterface::class)) { - $this->legacy_after_method_checks[] = $class; - } elseif (is_subclass_of($class, AfterMethodCallAnalysisInterface::class)) { + if (is_subclass_of($class, AfterMethodCallAnalysisInterface::class)) { $this->after_method_checks[] = $class; } - if (is_subclass_of($class, LegacyAfterFunctionCallAnalysisInterface::class)) { - $this->legacy_after_function_checks[] = $class; - } elseif (is_subclass_of($class, AfterFunctionCallAnalysisInterface::class)) { + if (is_subclass_of($class, AfterFunctionCallAnalysisInterface::class)) { $this->after_function_checks[] = $class; } - if (is_subclass_of($class, LegacyAfterEveryFunctionCallAnalysisInterface::class)) { - $this->legacy_after_every_function_checks[] = $class; - } elseif (is_subclass_of($class, AfterEveryFunctionCallAnalysisInterface::class)) { + if (is_subclass_of($class, AfterEveryFunctionCallAnalysisInterface::class)) { $this->after_every_function_checks[] = $class; } - if (is_subclass_of($class, LegacyAfterExpressionAnalysisInterface::class)) { - $this->legacy_after_expression_checks[] = $class; - } elseif (is_subclass_of($class, AfterExpressionAnalysisInterface::class)) { + if (is_subclass_of($class, AfterExpressionAnalysisInterface::class)) { $this->after_expression_checks[] = $class; } - if (is_subclass_of($class, LegacyAfterStatementAnalysisInterface::class)) { - $this->legacy_after_statement_checks[] = $class; - } elseif (is_subclass_of($class, AfterStatementAnalysisInterface::class)) { + if (is_subclass_of($class, AfterStatementAnalysisInterface::class)) { $this->after_statement_checks[] = $class; } - if (is_subclass_of($class, LegacyStringInterpreterInterface::class)) { - $this->legacy_string_interpreters[] = $class; - } elseif (is_subclass_of($class, StringInterpreterInterface::class)) { + if (is_subclass_of($class, StringInterpreterInterface::class)) { $this->string_interpreters[] = $class; } - if (is_subclass_of($class, LegacyAfterClassLikeExistenceCheckInterface::class)) { - $this->legacy_after_classlike_exists_checks[] = $class; - } elseif (is_subclass_of($class, AfterClassLikeExistenceCheckInterface::class)) { + if (is_subclass_of($class, AfterClassLikeExistenceCheckInterface::class)) { $this->after_classlike_exists_checks[] = $class; } - if (is_subclass_of($class, LegacyAfterClassLikeAnalysisInterface::class)) { - $this->legacy_after_classlike_checks[] = $class; - } elseif (is_subclass_of($class, AfterClassLikeAnalysisInterface::class)) { + if (is_subclass_of($class, AfterClassLikeAnalysisInterface::class)) { $this->after_classlike_checks[] = $class; } - if (is_subclass_of($class, LegacyAfterClassLikeVisitInterface::class)) { - $this->legacy_after_visit_classlikes[] = $class; - } elseif (is_subclass_of($class, AfterClassLikeVisitInterface::class)) { + if (is_subclass_of($class, AfterClassLikeVisitInterface::class)) { $this->after_visit_classlikes[] = $class; } - if (is_subclass_of($class, LegacyAfterCodebasePopulatedInterface::class)) { - $this->legacy_after_codebase_populated[] = $class; - } elseif (is_subclass_of($class, AfterCodebasePopulatedInterface::class)) { + if (is_subclass_of($class, AfterCodebasePopulatedInterface::class)) { $this->after_codebase_populated[] = $class; } - if (is_subclass_of($class, LegacyAfterAnalysisInterface::class)) { - $this->legacy_after_analysis[] = $class; - } elseif (is_subclass_of($class, AfterAnalysisInterface::class)) { + if (is_subclass_of($class, AfterAnalysisInterface::class)) { $this->after_analysis[] = $class; } - if (is_subclass_of($class, LegacyAfterFileAnalysisInterface::class)) { - $this->legacy_after_file_checks[] = $class; - } elseif (is_subclass_of($class, AfterFileAnalysisInterface::class)) { + if (is_subclass_of($class, AfterFileAnalysisInterface::class)) { $this->after_file_checks[] = $class; } - if (is_subclass_of($class, LegacyBeforeFileAnalysisInterface::class)) { - $this->legacy_before_file_checks[] = $class; - } elseif (is_subclass_of($class, BeforeFileAnalysisInterface::class)) { + if (is_subclass_of($class, BeforeFileAnalysisInterface::class)) { $this->before_file_checks[] = $class; } - if (is_subclass_of($class, LegacyAfterFunctionLikeAnalysisInterface::class)) { - $this->legacy_after_functionlike_checks[] = $class; - } elseif (is_subclass_of($class, AfterFunctionLikeAnalysisInterface::class)) { + if (is_subclass_of($class, AfterFunctionLikeAnalysisInterface::class)) { $this->after_functionlike_checks[] = $class; } @@ -306,7 +236,7 @@ public function registerClass(string $class): void public function hasAfterMethodCallAnalysisHandlers(): bool { - return count($this->after_method_checks) || count($this->legacy_after_method_checks); + return count($this->after_method_checks) > 0; } public function dispatchAfterMethodCallAnalysis(AfterMethodCallAnalysisEvent $event): void @@ -314,24 +244,6 @@ public function dispatchAfterMethodCallAnalysis(AfterMethodCallAnalysisEvent $ev foreach ($this->after_method_checks as $handler) { $handler::afterMethodCallAnalysis($event); } - - foreach ($this->legacy_after_method_checks as $handler) { - $file_replacements = $event->getFileReplacements(); - $return_type_candidate = $event->getReturnTypeCandidate(); - $handler::afterMethodCallAnalysis( - $event->getExpr(), - $event->getMethodId(), - $event->getAppearingMethodId(), - $event->getDeclaringMethodId(), - $event->getContext(), - $event->getStatementsSource(), - $event->getCodebase(), - $file_replacements, - $return_type_candidate - ); - $event->setFileReplacements($file_replacements); - $event->setReturnTypeCandidate($return_type_candidate); - } } public function dispatchAfterFunctionCallAnalysis(AfterFunctionCallAnalysisEvent $event): void @@ -339,20 +251,6 @@ public function dispatchAfterFunctionCallAnalysis(AfterFunctionCallAnalysisEvent foreach ($this->after_function_checks as $handler) { $handler::afterFunctionCallAnalysis($event); } - - foreach ($this->legacy_after_function_checks as $handler) { - $file_replacements = $event->getFileReplacements(); - $handler::afterFunctionCallAnalysis( - $event->getExpr(), - $event->getFunctionId(), - $event->getContext(), - $event->getStatementsSource(), - $event->getCodebase(), - $event->getReturnTypeCandidate(), - $file_replacements - ); - $event->setFileReplacements($file_replacements); - } } public function dispatchAfterEveryFunctionCallAnalysis(AfterEveryFunctionCallAnalysisEvent $event): void @@ -360,16 +258,6 @@ public function dispatchAfterEveryFunctionCallAnalysis(AfterEveryFunctionCallAna foreach ($this->after_every_function_checks as $handler) { $handler::afterEveryFunctionCallAnalysis($event); } - - foreach ($this->legacy_after_every_function_checks as $handler) { - $handler::afterEveryFunctionCallAnalysis( - $event->getExpr(), - $event->getFunctionId(), - $event->getContext(), - $event->getStatementsSource(), - $event->getCodebase() - ); - } } public function dispatchAfterExpressionAnalysis(AfterExpressionAnalysisEvent $event): ?bool @@ -380,20 +268,6 @@ public function dispatchAfterExpressionAnalysis(AfterExpressionAnalysisEvent $ev } } - foreach ($this->legacy_after_expression_checks as $handler) { - $file_replacements = $event->getFileReplacements(); - if ($handler::afterExpressionAnalysis( - $event->getExpr(), - $event->getContext(), - $event->getStatementsSource(), - $event->getCodebase(), - $file_replacements - ) === false) { - return false; - } - $event->setFileReplacements($file_replacements); - } - return null; } @@ -405,20 +279,6 @@ public function dispatchAfterStatementAnalysis(AfterStatementAnalysisEvent $even } } - foreach ($this->legacy_after_statement_checks as $handler) { - $file_replacements = $event->getFileReplacements(); - if ($handler::afterStatementAnalysis( - $event->getStmt(), - $event->getContext(), - $event->getStatementsSource(), - $event->getCodebase(), - $file_replacements - ) === false) { - return false; - } - $event->setFileReplacements($file_replacements); - } - return null; } @@ -430,12 +290,6 @@ public function dispatchStringInterpreter(StringInterpreterEvent $event): ?TLite } } - foreach ($this->legacy_string_interpreters as $handler) { - if ($type = $handler::getTypeFromValue($event->getValue())) { - return $type; - } - } - return null; } @@ -444,18 +298,6 @@ public function dispatchAfterClassLikeExistenceCheck(AfterClassLikeExistenceChec foreach ($this->after_classlike_exists_checks as $handler) { $handler::afterClassLikeExistenceCheck($event); } - - foreach ($this->legacy_after_classlike_exists_checks as $handler) { - $file_replacements = $event->getFileReplacements(); - $handler::afterClassLikeExistenceCheck( - $event->getFqClassName(), - $event->getCodeLocation(), - $event->getStatementsSource(), - $event->getCodebase(), - $file_replacements - ); - $event->setFileReplacements($file_replacements); - } } public function dispatchAfterClassLikeAnalysis(AfterClassLikeAnalysisEvent $event): ?bool @@ -466,26 +308,12 @@ public function dispatchAfterClassLikeAnalysis(AfterClassLikeAnalysisEvent $even } } - foreach ($this->legacy_after_classlike_checks as $handler) { - $file_replacements = $event->getFileReplacements(); - if ($handler::afterStatementAnalysis( - $event->getStmt(), - $event->getClasslikeStorage(), - $event->getStatementsSource(), - $event->getCodebase(), - $file_replacements - ) === false) { - return false; - } - $event->setFileReplacements($file_replacements); - } - return null; } public function hasAfterClassLikeVisitHandlers(): bool { - return count($this->after_visit_classlikes) || count($this->legacy_after_visit_classlikes); + return count($this->after_visit_classlikes) > 0; } public function dispatchAfterClassLikeVisit(AfterClassLikeVisitEvent $event): void @@ -493,18 +321,6 @@ public function dispatchAfterClassLikeVisit(AfterClassLikeVisitEvent $event): vo foreach ($this->after_visit_classlikes as $handler) { $handler::afterClassLikeVisit($event); } - - foreach ($this->legacy_after_visit_classlikes as $handler) { - $file_replacements = $event->getFileReplacements(); - $handler::afterClassLikeVisit( - $event->getStmt(), - $event->getStorage(), - $event->getStatementsSource(), - $event->getCodebase(), - $file_replacements - ); - $event->setFileReplacements($file_replacements); - } } public function dispatchAfterCodebasePopulated(AfterCodebasePopulatedEvent $event): void @@ -512,12 +328,6 @@ public function dispatchAfterCodebasePopulated(AfterCodebasePopulatedEvent $even foreach ($this->after_codebase_populated as $handler) { $handler::afterCodebasePopulated($event); } - - foreach ($this->legacy_after_codebase_populated as $handler) { - $handler::afterCodebasePopulated( - $event->getCodebase() - ); - } } public function dispatchAfterAnalysis(AfterAnalysisEvent $event): void @@ -525,16 +335,6 @@ public function dispatchAfterAnalysis(AfterAnalysisEvent $event): void foreach ($this->after_analysis as $handler) { $handler::afterAnalysis($event); } - - foreach ($this->legacy_after_analysis as $handler) { - /** @psalm-suppress MixedArgumentTypeCoercion due to Psalm bug */ - $handler::afterAnalysis( - $event->getCodebase(), - $event->getIssues(), - $event->getBuildInfo(), - $event->getSourceControlInfo() - ); - } } public function dispatchAfterFileAnalysis(AfterFileAnalysisEvent $event): void @@ -542,15 +342,6 @@ public function dispatchAfterFileAnalysis(AfterFileAnalysisEvent $event): void foreach ($this->after_file_checks as $handler) { $handler::afterAnalyzeFile($event); } - - foreach ($this->legacy_after_file_checks as $handler) { - $handler::afterAnalyzeFile( - $event->getStatementsSource(), - $event->getFileContext(), - $event->getFileStorage(), - $event->getCodebase() - ); - } } public function dispatchBeforeFileAnalysis(BeforeFileAnalysisEvent $event): void @@ -558,15 +349,6 @@ public function dispatchBeforeFileAnalysis(BeforeFileAnalysisEvent $event): void foreach ($this->before_file_checks as $handler) { $handler::beforeAnalyzeFile($event); } - - foreach ($this->legacy_before_file_checks as $handler) { - $handler::beforeAnalyzeFile( - $event->getStatementsSource(), - $event->getFileContext(), - $event->getFileStorage(), - $event->getCodebase() - ); - } } public function dispatchAfterFunctionLikeAnalysis(AfterFunctionLikeAnalysisEvent $event): ?bool @@ -577,20 +359,6 @@ public function dispatchAfterFunctionLikeAnalysis(AfterFunctionLikeAnalysisEvent } } - foreach ($this->legacy_after_functionlike_checks as $handler) { - $file_replacements = $event->getFileReplacements(); - if ($handler::afterStatementAnalysis( - $event->getStmt(), - $event->getClasslikeStorage(), - $event->getStatementsSource(), - $event->getCodebase(), - $file_replacements - ) === false) { - return false; - } - $event->setFileReplacements($file_replacements); - } - return null; } diff --git a/src/Psalm/Internal/Provider/FunctionExistenceProvider.php b/src/Psalm/Internal/Provider/FunctionExistenceProvider.php index 643f56b0231..17932060080 100644 --- a/src/Psalm/Internal/Provider/FunctionExistenceProvider.php +++ b/src/Psalm/Internal/Provider/FunctionExistenceProvider.php @@ -5,7 +5,6 @@ use Closure; use Psalm\Plugin\EventHandler\Event\FunctionExistenceProviderEvent; use Psalm\Plugin\EventHandler\FunctionExistenceProviderInterface; -use Psalm\Plugin\Hook\FunctionExistenceProviderInterface as LegacyFunctionExistenceProviderInterface; use Psalm\StatementsSource; use function is_subclass_of; @@ -24,21 +23,9 @@ class FunctionExistenceProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; } /** @@ -46,13 +33,7 @@ public function __construct() */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyFunctionExistenceProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'doesFunctionExist']); - - foreach ($class::getFunctionIds() as $function_id) { - $this->registerLegacyClosure($function_id, $callable); - } - } elseif (is_subclass_of($class, FunctionExistenceProviderInterface::class, true)) { + if (is_subclass_of($class, FunctionExistenceProviderInterface::class, true)) { $callable = Closure::fromCallable([$class, 'doesFunctionExist']); foreach ($class::getFunctionIds() as $function_id) { @@ -70,39 +51,15 @@ public function registerClosure(string $function_id, Closure $c): void self::$handlers[$function_id][] = $c; } - /** - * @param lowercase-string $function_id - * @param Closure( - * StatementsSource, - * string - * ): ?bool $c - */ - public function registerLegacyClosure(string $function_id, Closure $c): void - { - self::$legacy_handlers[$function_id][] = $c; - } - public function has(string $function_id): bool { - return isset(self::$handlers[strtolower($function_id)]) || - isset(self::$legacy_handlers[strtolower($function_id)]); + return isset(self::$handlers[strtolower($function_id)]); } public function doesFunctionExist( StatementsSource $statements_source, string $function_id ): ?bool { - foreach (self::$legacy_handlers[strtolower($function_id)] ?? [] as $function_handler) { - $function_exists = $function_handler( - $statements_source, - $function_id - ); - - if ($function_exists !== null) { - return $function_exists; - } - } - foreach (self::$handlers[strtolower($function_id)] ?? [] as $function_handler) { $event = new FunctionExistenceProviderEvent( $statements_source, diff --git a/src/Psalm/Internal/Provider/FunctionParamsProvider.php b/src/Psalm/Internal/Provider/FunctionParamsProvider.php index df82e60d9fa..3cf7493ddcd 100644 --- a/src/Psalm/Internal/Provider/FunctionParamsProvider.php +++ b/src/Psalm/Internal/Provider/FunctionParamsProvider.php @@ -8,11 +8,9 @@ use Psalm\Context; use Psalm\Plugin\EventHandler\Event\FunctionParamsProviderEvent; use Psalm\Plugin\EventHandler\FunctionParamsProviderInterface; -use Psalm\Plugin\Hook\FunctionParamsProviderInterface as LegacyFunctionParamsProviderInterface; use Psalm\StatementsSource; use Psalm\Storage\FunctionLikeParameter; -use function is_subclass_of; use function strtolower; /** @@ -28,43 +26,20 @@ class FunctionParamsProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array, - * ?Context=, - * ?CodeLocation= - * ): ?array> - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; } /** - * @param class-string|class-string $class + * @param class-string $class */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyFunctionParamsProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'getFunctionParams']); + $callable = Closure::fromCallable([$class, 'getFunctionParams']); - foreach ($class::getFunctionIds() as $function_id) { - $this->registerLegacyClosure($function_id, $callable); - } - } elseif (is_subclass_of($class, FunctionParamsProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'getFunctionParams']); - - foreach ($class::getFunctionIds() as $function_id) { - $this->registerClosure($function_id, $callable); - } + foreach ($class::getFunctionIds() as $function_id) { + $this->registerClosure($function_id, $callable); } } @@ -76,24 +51,9 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void self::$handlers[strtolower($fq_classlike_name)][] = $c; } - /** - * @param Closure( - * StatementsSource, - * string, - * list, - * ?Context=, - * ?CodeLocation= - * ): ?array $c - */ - public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void - { - self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c; - } - public function has(string $fq_classlike_name): bool { - return isset(self::$handlers[strtolower($fq_classlike_name)]) || - isset(self::$legacy_handlers[strtolower($fq_classlike_name)]); + return isset(self::$handlers[strtolower($fq_classlike_name)]); } /** @@ -123,20 +83,6 @@ public function getFunctionParams( } } - foreach (self::$legacy_handlers[strtolower($function_id)] ?? [] as $class_handler) { - $result = $class_handler( - $statements_source, - $function_id, - $call_args, - $context, - $code_location - ); - - if ($result) { - return $result; - } - } - return null; } } diff --git a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php index 8e03671778a..06390ab8069 100644 --- a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php @@ -4,7 +4,6 @@ use Closure; use PhpParser; -use PhpParser\Node\Arg; use Psalm\CodeLocation; use Psalm\Context; use Psalm\Internal\Provider\ReturnTypeProvider\ArrayChunkReturnTypeProvider; @@ -41,7 +40,6 @@ use Psalm\Internal\Provider\ReturnTypeProvider\VersionCompareReturnTypeProvider; use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent; use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface; -use Psalm\Plugin\Hook\FunctionReturnTypeProviderInterface as LegacyFunctionReturnTypeProviderInterface; use Psalm\StatementsSource; use Psalm\Type\Union; @@ -61,24 +59,9 @@ class FunctionReturnTypeProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array, - * Context, - * CodeLocation - * ): ?Union> - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; $this->registerClass(ArrayChunkReturnTypeProvider::class); $this->registerClass(ArrayColumnReturnTypeProvider::class); @@ -119,13 +102,7 @@ public function __construct() */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyFunctionReturnTypeProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'getFunctionReturnType']); - - foreach ($class::getFunctionIds() as $function_id) { - $this->registerLegacyClosure($function_id, $callable); - } - } elseif (is_subclass_of($class, FunctionReturnTypeProviderInterface::class, true)) { + if (is_subclass_of($class, FunctionReturnTypeProviderInterface::class, true)) { $callable = Closure::fromCallable([$class, 'getFunctionReturnType']); foreach ($class::getFunctionIds() as $function_id) { @@ -143,25 +120,9 @@ public function registerClosure(string $function_id, Closure $c): void self::$handlers[$function_id][] = $c; } - /** - * @param lowercase-string $function_id - * @param Closure( - * StatementsSource, - * non-empty-string, - * list, - * Context, - * CodeLocation - * ): ?Union $c - */ - public function registerLegacyClosure(string $function_id, Closure $c): void - { - self::$legacy_handlers[$function_id][] = $c; - } - public function has(string $function_id): bool { - return isset(self::$handlers[strtolower($function_id)]) || - isset(self::$legacy_handlers[strtolower($function_id)]); + return isset(self::$handlers[strtolower($function_id)]); } /** @@ -174,20 +135,6 @@ public function getReturnType( Context $context, CodeLocation $code_location ): ?Union { - foreach (self::$legacy_handlers[strtolower($function_id)] ?? [] as $function_handler) { - $return_type = $function_handler( - $statements_source, - $function_id, - $stmt->getArgs(), - $context, - $code_location - ); - - if ($return_type) { - return $return_type; - } - } - foreach (self::$handlers[strtolower($function_id)] ?? [] as $function_handler) { $event = new FunctionReturnTypeProviderEvent( $statements_source, diff --git a/src/Psalm/Internal/Provider/MethodExistenceProvider.php b/src/Psalm/Internal/Provider/MethodExistenceProvider.php index 695f22e2dca..a2f45ae0706 100644 --- a/src/Psalm/Internal/Provider/MethodExistenceProvider.php +++ b/src/Psalm/Internal/Provider/MethodExistenceProvider.php @@ -6,10 +6,8 @@ use Psalm\CodeLocation; use Psalm\Plugin\EventHandler\Event\MethodExistenceProviderEvent; use Psalm\Plugin\EventHandler\MethodExistenceProviderInterface; -use Psalm\Plugin\Hook\MethodExistenceProviderInterface as LegacyMethodExistenceProviderInterface; use Psalm\StatementsSource; -use function is_subclass_of; use function strtolower; /** @@ -25,42 +23,20 @@ class MethodExistenceProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; } /** - * @param class-string|class-string $class + * @param class-string $class */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyMethodExistenceProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'doesMethodExist']); + $callable = Closure::fromCallable([$class, 'doesMethodExist']); - foreach ($class::getClassLikeNames() as $fq_classlike_name) { - $this->registerLegacyClosure($fq_classlike_name, $callable); - } - } elseif (is_subclass_of($class, MethodExistenceProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'doesMethodExist']); - - foreach ($class::getClassLikeNames() as $fq_classlike_name) { - $this->registerClosure($fq_classlike_name, $callable); - } + foreach ($class::getClassLikeNames() as $fq_classlike_name) { + $this->registerClosure($fq_classlike_name, $callable); } } @@ -72,23 +48,9 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void self::$handlers[strtolower($fq_classlike_name)][] = $c; } - /** - * @param Closure( - * string, - * string, - * ?StatementsSource=, - * ?CodeLocation - * ): ?bool $c - */ - public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void - { - self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c; - } - public function has(string $fq_classlike_name): bool { - return isset(self::$handlers[strtolower($fq_classlike_name)]) || - isset(self::$legacy_handlers[strtolower($fq_classlike_name)]); + return isset(self::$handlers[strtolower($fq_classlike_name)]); } public function doesMethodExist( @@ -97,19 +59,6 @@ public function doesMethodExist( ?StatementsSource $source = null, ?CodeLocation $code_location = null ): ?bool { - foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $method_handler) { - $method_exists = $method_handler( - $fq_classlike_name, - $method_name_lowercase, - $source, - $code_location - ); - - if ($method_exists !== null) { - return $method_exists; - } - } - foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $method_handler) { $event = new MethodExistenceProviderEvent( $fq_classlike_name, diff --git a/src/Psalm/Internal/Provider/MethodParamsProvider.php b/src/Psalm/Internal/Provider/MethodParamsProvider.php index b8a44c7a906..537979ea281 100644 --- a/src/Psalm/Internal/Provider/MethodParamsProvider.php +++ b/src/Psalm/Internal/Provider/MethodParamsProvider.php @@ -9,7 +9,6 @@ use Psalm\Internal\Provider\ReturnTypeProvider\PdoStatementSetFetchMode; use Psalm\Plugin\EventHandler\Event\MethodParamsProviderEvent; use Psalm\Plugin\EventHandler\MethodParamsProviderInterface; -use Psalm\Plugin\Hook\MethodParamsProviderInterface as LegacyMethodParamsProviderInterface; use Psalm\StatementsSource; use Psalm\Storage\FunctionLikeParameter; @@ -29,25 +28,9 @@ class MethodParamsProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array=, - * ?StatementsSource=, - * ?Context=, - * ?CodeLocation= - * ): ?array> - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; $this->registerClass(PdoStatementSetFetchMode::class); } @@ -57,13 +40,7 @@ public function __construct() */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyMethodParamsProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'getMethodParams']); - - foreach ($class::getClassLikeNames() as $fq_classlike_name) { - $this->registerLegacyClosure($fq_classlike_name, $callable); - } - } elseif (is_subclass_of($class, MethodParamsProviderInterface::class, true)) { + if (is_subclass_of($class, MethodParamsProviderInterface::class, true)) { $callable = Closure::fromCallable([$class, 'getMethodParams']); foreach ($class::getClassLikeNames() as $fq_classlike_name) { @@ -80,25 +57,9 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void self::$handlers[strtolower($fq_classlike_name)][] = $c; } - /** - * @param Closure( - * string, - * string, - * ?list=, - * ?StatementsSource=, - * ?Context=, - * ?CodeLocation= - * ): ?array $c - */ - public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void - { - self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c; - } - public function has(string $fq_classlike_name): bool { - return isset(self::$handlers[strtolower($fq_classlike_name)]) || - isset(self::$legacy_handlers[strtolower($fq_classlike_name)]); + return isset(self::$handlers[strtolower($fq_classlike_name)]); } /** @@ -114,21 +75,6 @@ public function getMethodParams( ?Context $context = null, ?CodeLocation $code_location = null ): ?array { - foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $class_handler) { - $result = $class_handler( - $fq_classlike_name, - $method_name_lowercase, - $call_args, - $statements_source, - $context, - $code_location - ); - - if ($result !== null) { - return $result; - } - } - foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $class_handler) { $event = new MethodParamsProviderEvent( $fq_classlike_name, diff --git a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php index 08fe1f8c0c9..197a4564f57 100644 --- a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php @@ -13,7 +13,6 @@ use Psalm\Internal\Provider\ReturnTypeProvider\SimpleXmlElementAsXml; use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent; use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface; -use Psalm\Plugin\Hook\MethodReturnTypeProviderInterface as LegacyMethodReturnTypeProviderInterface; use Psalm\StatementsSource; use Psalm\Type\Union; @@ -33,28 +32,9 @@ class MethodReturnTypeProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array, - * Context, - * CodeLocation, - * ?array=, - * ?string=, - * ?lowercase-string= - * ): ?Union> - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; $this->registerClass(DomNodeAppendChild::class); $this->registerClass(ImagickPixelColorReturnTypeProvider::class); @@ -68,13 +48,7 @@ public function __construct() */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyMethodReturnTypeProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'getMethodReturnType']); - - foreach ($class::getClassLikeNames() as $fq_classlike_name) { - $this->registerLegacyClosure($fq_classlike_name, $callable); - } - } elseif (is_subclass_of($class, MethodReturnTypeProviderInterface::class, true)) { + if (is_subclass_of($class, MethodReturnTypeProviderInterface::class, true)) { $callable = Closure::fromCallable([$class, 'getMethodReturnType']); foreach ($class::getClassLikeNames() as $fq_classlike_name) { @@ -91,29 +65,9 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void self::$handlers[strtolower($fq_classlike_name)][] = $c; } - /** - * @param Closure( - * StatementsSource, - * string, - * lowercase-string, - * list, - * Context, - * CodeLocation, - * ?array=, - * ?string=, - * ?lowercase-string= - * ): ?Union $c - * - */ - public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void - { - self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c; - } - public function has(string $fq_classlike_name): bool { - return isset(self::$handlers[strtolower($fq_classlike_name)]) || - isset(self::$legacy_handlers[strtolower($fq_classlike_name)]); + return isset(self::$handlers[strtolower($fq_classlike_name)]); } /** @@ -131,24 +85,6 @@ public function getReturnType( ?string $called_fq_classlike_name = null, ?string $called_method_name = null ): ?Union { - foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $class_handler) { - $result = $class_handler( - $statements_source, - $fq_classlike_name, - strtolower($method_name), - $stmt->getArgs(), - $context, - $code_location, - $template_type_parameters, - $called_fq_classlike_name, - $called_method_name ? strtolower($called_method_name) : null - ); - - if ($result) { - return $result; - } - } - foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $class_handler) { $event = new MethodReturnTypeProviderEvent( $statements_source, diff --git a/src/Psalm/Internal/Provider/MethodVisibilityProvider.php b/src/Psalm/Internal/Provider/MethodVisibilityProvider.php index 6942c24e169..e090f38739f 100644 --- a/src/Psalm/Internal/Provider/MethodVisibilityProvider.php +++ b/src/Psalm/Internal/Provider/MethodVisibilityProvider.php @@ -7,7 +7,6 @@ use Psalm\Context; use Psalm\Plugin\EventHandler\Event\MethodVisibilityProviderEvent; use Psalm\Plugin\EventHandler\MethodVisibilityProviderInterface; -use Psalm\Plugin\Hook\MethodVisibilityProviderInterface as LegacyMethodVisibilityProviderInterface; use Psalm\StatementsSource; use function is_subclass_of; @@ -26,24 +25,9 @@ class MethodVisibilityProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; } /** @@ -52,13 +36,7 @@ public function __construct() */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyMethodVisibilityProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'isMethodVisible']); - - foreach ($class::getClassLikeNames() as $fq_classlike_name) { - $this->registerLegacyClosure($fq_classlike_name, $callable); - } - } elseif (is_subclass_of($class, MethodVisibilityProviderInterface::class, true)) { + if (is_subclass_of($class, MethodVisibilityProviderInterface::class, true)) { $callable = Closure::fromCallable([$class, 'isMethodVisible']); foreach ($class::getClassLikeNames() as $fq_classlike_name) { @@ -75,24 +53,9 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void self::$handlers[strtolower($fq_classlike_name)][] = $c; } - /** - * @param Closure( - * StatementsSource, - * string, - * string, - * Context, - * ?CodeLocation - * ): ?bool $c - */ - public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void - { - self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c; - } - public function has(string $fq_classlike_name): bool { - return isset(self::$handlers[strtolower($fq_classlike_name)]) || - isset(self::$legacy_handlers[strtolower($fq_classlike_name)]); + return isset(self::$handlers[strtolower($fq_classlike_name)]); } public function isMethodVisible( @@ -102,20 +65,6 @@ public function isMethodVisible( Context $context, ?CodeLocation $code_location = null ): ?bool { - foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $method_handler) { - $method_visible = $method_handler( - $source, - $fq_classlike_name, - $method_name, - $context, - $code_location - ); - - if ($method_visible !== null) { - return $method_visible; - } - } - foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $method_handler) { $event = new MethodVisibilityProviderEvent( $source, diff --git a/src/Psalm/Internal/Provider/PropertyExistenceProvider.php b/src/Psalm/Internal/Provider/PropertyExistenceProvider.php index 93e23b2de8b..9065b3fb99e 100644 --- a/src/Psalm/Internal/Provider/PropertyExistenceProvider.php +++ b/src/Psalm/Internal/Provider/PropertyExistenceProvider.php @@ -7,7 +7,6 @@ use Psalm\Context; use Psalm\Plugin\EventHandler\Event\PropertyExistenceProviderEvent; use Psalm\Plugin\EventHandler\PropertyExistenceProviderInterface; -use Psalm\Plugin\Hook\PropertyExistenceProviderInterface as LegacyPropertyExistenceProviderInterface; use Psalm\StatementsSource; use function is_subclass_of; @@ -26,25 +25,9 @@ class PropertyExistenceProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; } /** @@ -53,13 +36,7 @@ public function __construct() */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyPropertyExistenceProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'doesPropertyExist']); - - foreach ($class::getClassLikeNames() as $fq_classlike_name) { - $this->registerLegacyClosure($fq_classlike_name, $callable); - } - } elseif (is_subclass_of($class, PropertyExistenceProviderInterface::class, true)) { + if (is_subclass_of($class, PropertyExistenceProviderInterface::class, true)) { $callable = Closure::fromCallable([$class, 'doesPropertyExist']); foreach ($class::getClassLikeNames() as $fq_classlike_name) { @@ -76,25 +53,9 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void self::$handlers[strtolower($fq_classlike_name)][] = $c; } - /** - * @param Closure( - * string, - * string, - * bool, - * ?StatementsSource=, - * ?Context=, - * ?CodeLocation= - * ): ?bool $c - */ - public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void - { - self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c; - } - public function has(string $fq_classlike_name): bool { - return isset(self::$handlers[strtolower($fq_classlike_name)]) || - isset(self::$legacy_handlers[strtolower($fq_classlike_name)]); + return isset(self::$handlers[strtolower($fq_classlike_name)]); } public function doesPropertyExist( @@ -105,21 +66,6 @@ public function doesPropertyExist( ?Context $context = null, ?CodeLocation $code_location = null ): ?bool { - foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) { - $property_exists = $property_handler( - $fq_classlike_name, - $property_name, - $read_mode, - $source, - $context, - $code_location - ); - - if ($property_exists !== null) { - return $property_exists; - } - } - foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) { $event = new PropertyExistenceProviderEvent( $fq_classlike_name, diff --git a/src/Psalm/Internal/Provider/PropertyTypeProvider.php b/src/Psalm/Internal/Provider/PropertyTypeProvider.php index 788f839a183..e24509291e0 100644 --- a/src/Psalm/Internal/Provider/PropertyTypeProvider.php +++ b/src/Psalm/Internal/Provider/PropertyTypeProvider.php @@ -7,7 +7,6 @@ use Psalm\Internal\Provider\PropertyTypeProvider\DomDocumentPropertyTypeProvider; use Psalm\Plugin\EventHandler\Event\PropertyTypeProviderEvent; use Psalm\Plugin\EventHandler\PropertyTypeProviderInterface; -use Psalm\Plugin\Hook\PropertyTypeProviderInterface as LegacyPropertyTypeProviderInterface; use Psalm\StatementsSource; use Psalm\Type\Union; @@ -27,24 +26,9 @@ class PropertyTypeProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; $this->registerClass(DomDocumentPropertyTypeProvider::class); } @@ -54,13 +38,7 @@ public function __construct() */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyPropertyTypeProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'getPropertyType']); - - foreach ($class::getClassLikeNames() as $fq_classlike_name) { - $this->registerLegacyClosure($fq_classlike_name, $callable); - } - } elseif (is_subclass_of($class, PropertyTypeProviderInterface::class, true)) { + if (is_subclass_of($class, PropertyTypeProviderInterface::class, true)) { $callable = Closure::fromCallable([$class, 'getPropertyType']); foreach ($class::getClassLikeNames() as $fq_classlike_name) { @@ -77,24 +55,9 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void self::$handlers[strtolower($fq_classlike_name)][] = $c; } - /** - * @param Closure( - * string, - * string, - * bool, - * ?StatementsSource=, - * ?Context= - * ): ?Union $c - */ - public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void - { - self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c; - } - public function has(string $fq_classlike_name): bool { - return isset(self::$handlers[strtolower($fq_classlike_name)]) || - isset(self::$legacy_handlers[strtolower($fq_classlike_name)]); + return isset(self::$handlers[strtolower($fq_classlike_name)]); } public function getPropertyType( @@ -109,20 +72,6 @@ public function getPropertyType( $source->addSuppressedIssues(['NonInvariantDocblockPropertyType']); } - foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) { - $property_type = $property_handler( - $fq_classlike_name, - $property_name, - $read_mode, - $source, - $context - ); - - if ($property_type !== null) { - return $property_type; - } - } - foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) { $event = new PropertyTypeProviderEvent( $fq_classlike_name, diff --git a/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php b/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php index 0d63450b4d3..de2ba125ad2 100644 --- a/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php +++ b/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php @@ -7,10 +7,8 @@ use Psalm\Context; use Psalm\Plugin\EventHandler\Event\PropertyVisibilityProviderEvent; use Psalm\Plugin\EventHandler\PropertyVisibilityProviderInterface; -use Psalm\Plugin\Hook\PropertyVisibilityProviderInterface as LegacyPropertyVisibilityProviderInterface; use Psalm\StatementsSource; -use function is_subclass_of; use function strtolower; /** @@ -26,45 +24,20 @@ class PropertyVisibilityProvider */ private static $handlers = []; - /** - * @var array< - * lowercase-string, - * array - * > - */ - private static $legacy_handlers = []; - public function __construct() { self::$handlers = []; - self::$legacy_handlers = []; } /** - * @param class-string - * |class-string $class + * @param class-string $class */ public function registerClass(string $class): void { - if (is_subclass_of($class, LegacyPropertyVisibilityProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'isPropertyVisible']); - - foreach ($class::getClassLikeNames() as $fq_classlike_name) { - $this->registerLegacyClosure($fq_classlike_name, $callable); - } - } elseif (is_subclass_of($class, PropertyVisibilityProviderInterface::class, true)) { - $callable = Closure::fromCallable([$class, 'isPropertyVisible']); + $callable = Closure::fromCallable([$class, 'isPropertyVisible']); - foreach ($class::getClassLikeNames() as $fq_classlike_name) { - $this->registerClosure($fq_classlike_name, $callable); - } + foreach ($class::getClassLikeNames() as $fq_classlike_name) { + $this->registerClosure($fq_classlike_name, $callable); } } @@ -76,25 +49,9 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void self::$handlers[strtolower($fq_classlike_name)][] = $c; } - /** - * @param Closure( - * StatementsSource, - * string, - * string, - * bool, - * Context, - * CodeLocation - * ): ?bool $c - */ - public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void - { - self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c; - } - public function has(string $fq_classlike_name): bool { - return isset(self::$handlers[strtolower($fq_classlike_name)]) || - isset(self::$legacy_handlers[strtolower($fq_classlike_name)]); + return isset(self::$handlers[strtolower($fq_classlike_name)]); } public function isPropertyVisible( @@ -105,21 +62,6 @@ public function isPropertyVisible( Context $context, CodeLocation $code_location ): ?bool { - foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) { - $property_visible = $property_handler( - $source, - $fq_classlike_name, - $property_name, - $read_mode, - $context, - $code_location - ); - - if ($property_visible !== null) { - return $property_visible; - } - } - foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) { $event = new PropertyVisibilityProviderEvent( $source, diff --git a/src/Psalm/IssueBuffer.php b/src/Psalm/IssueBuffer.php index b9c5308d872..45632b51023 100644 --- a/src/Psalm/IssueBuffer.php +++ b/src/Psalm/IssueBuffer.php @@ -635,9 +635,7 @@ function (IssueData $d1, IssueData $d2): int { } - if ($codebase->config->eventDispatcher->after_analysis - || $codebase->config->eventDispatcher->legacy_after_analysis - ) { + if ($codebase->config->eventDispatcher->after_analysis) { $source_control_info = null; $build_info = (new BuildInfoCollector(self::$server))->collect(); diff --git a/src/Psalm/Plugin/Hook/AfterAnalysisInterface.php b/src/Psalm/Plugin/Hook/AfterAnalysisInterface.php deleted file mode 100644 index 2e399d4d32d..00000000000 --- a/src/Psalm/Plugin/Hook/AfterAnalysisInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -> $issues - */ - public static function afterAnalysis( - Codebase $codebase, - array $issues, - array $build_info, - ?SourceControlInfo $source_control_info = null - ): void; -} diff --git a/src/Psalm/Plugin/Hook/AfterClassLikeAnalysisInterface.php b/src/Psalm/Plugin/Hook/AfterClassLikeAnalysisInterface.php deleted file mode 100644 index c910400e0ac..00000000000 --- a/src/Psalm/Plugin/Hook/AfterClassLikeAnalysisInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - */ - public static function getFunctionIds(): array; - - /** - * Use this hook for informing whether or not a global function exists. If you know the function does - * not exist, return false. If you aren't sure if it exists or not, return null and the default analysis - * will continue to determine if the function actually exists. - * - */ - public static function doesFunctionExist( - StatementsSource $statements_source, - string $function_id - ): ?bool; -} diff --git a/src/Psalm/Plugin/Hook/FunctionParamsProviderInterface.php b/src/Psalm/Plugin/Hook/FunctionParamsProviderInterface.php deleted file mode 100644 index 8b23d82968c..00000000000 --- a/src/Psalm/Plugin/Hook/FunctionParamsProviderInterface.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ - public static function getFunctionIds(): array; - - /** - * @param list $call_args - * - * @return ?array - */ - public static function getFunctionParams( - StatementsSource $statements_source, - string $function_id, - array $call_args, - ?Context $context = null, - ?CodeLocation $code_location = null - ): ?array; -} diff --git a/src/Psalm/Plugin/Hook/FunctionReturnTypeProviderInterface.php b/src/Psalm/Plugin/Hook/FunctionReturnTypeProviderInterface.php deleted file mode 100644 index 125d779f69a..00000000000 --- a/src/Psalm/Plugin/Hook/FunctionReturnTypeProviderInterface.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ - public static function getFunctionIds(): array; - - /** - * Use this hook for providing custom return type logic. If this plugin does not know what a function should - * return but another plugin may be able to determine the type, return null. Otherwise return a mixed union type - * if something should be returned, but can't be more specific. - * - * @param list $call_args - */ - public static function getFunctionReturnType( - StatementsSource $statements_source, - string $function_id, - array $call_args, - Context $context, - CodeLocation $code_location - ): ?Union; -} diff --git a/src/Psalm/Plugin/Hook/MethodExistenceProviderInterface.php b/src/Psalm/Plugin/Hook/MethodExistenceProviderInterface.php deleted file mode 100644 index 2964fe60445..00000000000 --- a/src/Psalm/Plugin/Hook/MethodExistenceProviderInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - */ - public static function getClassLikeNames(): array; - - /** - * Use this hook for informing whether or not a method exists on a given object. If you know the method does - * not exist, return false. If you aren't sure if it exists or not, return null and the default analysis will - * continue to determine if the method actually exists. - */ - public static function doesMethodExist( - string $fq_classlike_name, - string $method_name_lowercase, - ?StatementsSource $source = null, - ?CodeLocation $code_location = null - ): ?bool; -} diff --git a/src/Psalm/Plugin/Hook/MethodParamsProviderInterface.php b/src/Psalm/Plugin/Hook/MethodParamsProviderInterface.php deleted file mode 100644 index 680c60a8ff7..00000000000 --- a/src/Psalm/Plugin/Hook/MethodParamsProviderInterface.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ - public static function getClassLikeNames(): array; - - /** - * @param list $call_args - * - * @return ?array - */ - public static function getMethodParams( - string $fq_classlike_name, - string $method_name_lowercase, - ?array $call_args = null, - ?StatementsSource $statements_source = null, - ?Context $context = null, - ?CodeLocation $code_location = null - ): ?array; -} diff --git a/src/Psalm/Plugin/Hook/MethodReturnTypeProviderInterface.php b/src/Psalm/Plugin/Hook/MethodReturnTypeProviderInterface.php deleted file mode 100644 index c39a61ce89f..00000000000 --- a/src/Psalm/Plugin/Hook/MethodReturnTypeProviderInterface.php +++ /dev/null @@ -1,40 +0,0 @@ - - */ - public static function getClassLikeNames(): array; - - /** - * Use this hook for providing custom return type logic. If this plugin does not know what a method should return - * but another plugin may be able to determine the type, return null. Otherwise return a mixed union type if - * something should be returned, but can't be more specific. - * - * @param list $call_args - * @param ?array $template_type_parameters - * @param lowercase-string $method_name_lowercase - * @param lowercase-string $called_method_name_lowercase - */ - public static function getMethodReturnType( - StatementsSource $source, - string $fq_classlike_name, - string $method_name_lowercase, - array $call_args, - Context $context, - CodeLocation $code_location, - ?array $template_type_parameters = null, - ?string $called_fq_classlike_name = null, - ?string $called_method_name_lowercase = null - ): ?Union; -} diff --git a/src/Psalm/Plugin/Hook/MethodVisibilityProviderInterface.php b/src/Psalm/Plugin/Hook/MethodVisibilityProviderInterface.php deleted file mode 100644 index bf6113521e0..00000000000 --- a/src/Psalm/Plugin/Hook/MethodVisibilityProviderInterface.php +++ /dev/null @@ -1,24 +0,0 @@ - - */ - public static function getClassLikeNames(): array; - - public static function isMethodVisible( - StatementsSource $source, - string $fq_classlike_name, - string $method_name_lowercase, - Context $context, - ?CodeLocation $code_location = null - ): ?bool; -} diff --git a/src/Psalm/Plugin/Hook/PropertyExistenceProviderInterface.php b/src/Psalm/Plugin/Hook/PropertyExistenceProviderInterface.php deleted file mode 100644 index 6debbb96b4a..00000000000 --- a/src/Psalm/Plugin/Hook/PropertyExistenceProviderInterface.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ - public static function getClassLikeNames(): array; - - /** - * Use this hook for informing whether or not a property exists on a given object. If you know the property does - * not exist, return false. If you aren't sure if it exists or not, return null and the default analysis will - * continue to determine if the property actually exists. - * - */ - public static function doesPropertyExist( - string $fq_classlike_name, - string $property_name, - bool $read_mode, - ?StatementsSource $source = null, - ?Context $context = null, - ?CodeLocation $code_location = null - ): ?bool; -} diff --git a/src/Psalm/Plugin/Hook/PropertyTypeProviderInterface.php b/src/Psalm/Plugin/Hook/PropertyTypeProviderInterface.php deleted file mode 100644 index 0e3f18fc280..00000000000 --- a/src/Psalm/Plugin/Hook/PropertyTypeProviderInterface.php +++ /dev/null @@ -1,24 +0,0 @@ - - */ - public static function getClassLikeNames(): array; - - public static function getPropertyType( - string $fq_classlike_name, - string $property_name, - bool $read_mode, - ?StatementsSource $source = null, - ?Context $context = null - ): ?Union; -} diff --git a/src/Psalm/Plugin/Hook/PropertyVisibilityProviderInterface.php b/src/Psalm/Plugin/Hook/PropertyVisibilityProviderInterface.php deleted file mode 100644 index 650b9447e1f..00000000000 --- a/src/Psalm/Plugin/Hook/PropertyVisibilityProviderInterface.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public static function getClassLikeNames(): array; - - public static function isPropertyVisible( - StatementsSource $source, - string $fq_classlike_name, - string $property_name, - bool $read_mode, - Context $context, - CodeLocation $code_location - ): ?bool; -} diff --git a/src/Psalm/Plugin/Hook/StringInterpreterInterface.php b/src/Psalm/Plugin/Hook/StringInterpreterInterface.php deleted file mode 100644 index 976918ee5b4..00000000000 --- a/src/Psalm/Plugin/Hook/StringInterpreterInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -config->eventDispatcher->registerClass($handler); - if (is_subclass_of($handler, LegacyPropertyExistenceProviderInterface::class) || - is_subclass_of($handler, PropertyExistenceProviderInterface::class) - ) { + if (is_subclass_of($handler, PropertyExistenceProviderInterface::class)) { $this->codebase->properties->property_existence_provider->registerClass($handler); } - if (is_subclass_of($handler, LegacyPropertyVisibilityProviderInterface::class) || - is_subclass_of($handler, PropertyVisibilityProviderInterface::class) - ) { + if (is_subclass_of($handler, PropertyVisibilityProviderInterface::class)) { $this->codebase->properties->property_visibility_provider->registerClass($handler); } - if (is_subclass_of($handler, LegacyPropertyTypeProviderInterface::class) || - is_subclass_of($handler, PropertyTypeProviderInterface::class) - ) { + if (is_subclass_of($handler, PropertyTypeProviderInterface::class)) { $this->codebase->properties->property_type_provider->registerClass($handler); } - if (is_subclass_of($handler, LegacyMethodExistenceProviderInterface::class) || - is_subclass_of($handler, MethodExistenceProviderInterface::class) - ) { + if (is_subclass_of($handler, MethodExistenceProviderInterface::class)) { $this->codebase->methods->existence_provider->registerClass($handler); } - if (is_subclass_of($handler, LegacyMethodVisibilityProviderInterface::class) || - is_subclass_of($handler, MethodVisibilityProviderInterface::class) - ) { + if (is_subclass_of($handler, MethodVisibilityProviderInterface::class)) { $this->codebase->methods->visibility_provider->registerClass($handler); } - if (is_subclass_of($handler, LegacyMethodReturnTypeProviderInterface::class) || - is_subclass_of($handler, MethodReturnTypeProviderInterface::class) - ) { + if (is_subclass_of($handler, MethodReturnTypeProviderInterface::class)) { $this->codebase->methods->return_type_provider->registerClass($handler); } - if (is_subclass_of($handler, LegacyMethodParamsProviderInterface::class) || - is_subclass_of($handler, MethodParamsProviderInterface::class) - ) { + if (is_subclass_of($handler, MethodParamsProviderInterface::class)) { $this->codebase->methods->params_provider->registerClass($handler); } - if (is_subclass_of($handler, LegacyFunctionExistenceProviderInterface::class) || - is_subclass_of($handler, FunctionExistenceProviderInterface::class) - ) { + if (is_subclass_of($handler, FunctionExistenceProviderInterface::class)) { $this->codebase->functions->existence_provider->registerClass($handler); } - if (is_subclass_of($handler, LegacyFunctionParamsProviderInterface::class) || - is_subclass_of($handler, FunctionParamsProviderInterface::class) - ) { + if (is_subclass_of($handler, FunctionParamsProviderInterface::class)) { $this->codebase->functions->params_provider->registerClass($handler); } - if (is_subclass_of($handler, LegacyFunctionReturnTypeProviderInterface::class) || - is_subclass_of($handler, FunctionReturnTypeProviderInterface::class) - ) { + if (is_subclass_of($handler, FunctionReturnTypeProviderInterface::class)) { $this->codebase->functions->return_type_provider->registerClass($handler); } } diff --git a/tests/Config/PluginTest.php b/tests/Config/PluginTest.php index 0734f7e0f06..096d51bcdc8 100644 --- a/tests/Config/PluginTest.php +++ b/tests/Config/PluginTest.php @@ -4,13 +4,9 @@ use InvalidArgumentException; use PHPUnit\Framework\MockObject\MockObject; -use PhpParser\Node\Expr; -use PhpParser\Node\Stmt\ClassLike; -use Psalm\Codebase; use Psalm\Config; use Psalm\Context; use Psalm\Exception\CodeException; -use Psalm\FileSource; use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\IncludeCollector; use Psalm\Internal\Provider\FakeFileProvider; @@ -21,18 +17,13 @@ use Psalm\Plugin\EventHandler\AfterEveryFunctionCallAnalysisInterface; use Psalm\Plugin\EventHandler\Event\AfterCodebasePopulatedEvent; use Psalm\Plugin\EventHandler\Event\AfterEveryFunctionCallAnalysisEvent; -use Psalm\Plugin\Hook\AfterClassLikeVisitInterface as LegacyAfterClassLikeVisitInterface; -use Psalm\Plugin\Hook\AfterMethodCallAnalysisInterface as LegacyAfterMethodCallAnalysisInterface; use Psalm\PluginRegistrationSocket; use Psalm\Report; use Psalm\Report\ReportOptions; -use Psalm\StatementsSource; -use Psalm\Storage\ClassLikeStorage; use Psalm\Test\Config\Plugin\Hook\StringProvider\TSqlSelectString; use Psalm\Tests\Internal\Provider\FakeParserCacheProvider; use Psalm\Tests\TestCase; use Psalm\Tests\TestConfig; -use Psalm\Type\Union; use stdClass; use function define; @@ -572,86 +563,6 @@ public static function afterCodebasePopulated(AfterCodebasePopulatedEvent $event ); } - public function testAfterMethodCallAnalysisLegacyHookIsLoaded(): void - { - $this->project_analyzer = $this->getProjectAnalyzerWithConfig( - TestConfig::loadFromXML( - dirname(__DIR__, 2) . DIRECTORY_SEPARATOR, - ' - - - - - ' - ) - ); - - $hook = new class implements LegacyAfterMethodCallAnalysisInterface { - public static function afterMethodCallAnalysis( - Expr $expr, - string $method_id, - string $appearing_method_id, - string $declaring_method_id, - Context $context, - StatementsSource $statements_source, - Codebase $codebase, - array &$file_replacements = [], - Union &$return_type_candidate = null - ): void { - } - }; - - $codebase = $this->project_analyzer->getCodebase(); - - $config = $codebase->config; - - (new PluginRegistrationSocket($config, $codebase))->registerHooksFromClass(get_class($hook)); - - $this->assertTrue($this->project_analyzer->getCodebase()->config->eventDispatcher->hasAfterMethodCallAnalysisHandlers()); - } - - public function testAfterClassLikeAnalysisLegacyHookIsLoaded(): void - { - $this->project_analyzer = $this->getProjectAnalyzerWithConfig( - TestConfig::loadFromXML( - dirname(__DIR__, 2) . DIRECTORY_SEPARATOR, - ' - - - - - ' - ) - ); - - $hook = new class implements LegacyAfterClassLikeVisitInterface { - /** - * @return void - * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint - */ - public static function afterClassLikeVisit( - ClassLike $stmt, - ClassLikeStorage $storage, - FileSource $statements_source, - Codebase $codebase, - array &$file_replacements = [] - ) { - } - }; - - $codebase = $this->project_analyzer->getCodebase(); - - $config = $codebase->config; - - (new PluginRegistrationSocket($config, $codebase))->registerHooksFromClass(get_class($hook)); - - $this->assertTrue($this->project_analyzer->getCodebase()->config->eventDispatcher->hasAfterClassLikeVisitHandlers()); - } - public function testPropertyProviderHooks(): void { require_once __DIR__ . '/Plugin/PropertyPlugin.php'; From 0a991e34670c5955795a7224f9573c4a49c6a03c Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 18:34:54 +0200 Subject: [PATCH 28/78] Documented removal of legacy hook interfaces --- UPGRADING.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/UPGRADING.md b/UPGRADING.md index 2ee7fbbc47d..8b804f27960 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -127,3 +127,28 @@ - [BC] Property `Psalm\Config::$allow_phpstorm_generics` was removed - [BC] Property `Psalm\Config::$exit_functions` was removed - [BC] Method `Psalm\Type::getEmpty()` was removed + - [BC] Legacy hook interfaces have been removed: + - `Psalm\Plugin\Hook\MethodReturnTypeProviderInterface` + - `Psalm\Plugin\Hook\BeforeFileAnalysisInterface` + - `Psalm\Plugin\Hook\AfterFileAnalysisInterface` + - `Psalm\Plugin\Hook\AfterMethodCallAnalysisInterface` + - `Psalm\Plugin\Hook\AfterClassLikeVisitInterface` + - `Psalm\Plugin\Hook\StringInterpreterInterface` + - `Psalm\Plugin\Hook\AfterExpressionAnalysisInterface` + - `Psalm\Plugin\Hook\AfterEveryFunctionCallAnalysisInterface` + - `Psalm\Plugin\Hook\PropertyExistenceProviderInterface` + - `Psalm\Plugin\Hook\AfterFunctionLikeAnalysisInterface` + - `Psalm\Plugin\Hook\FunctionParamsProviderInterface` + - `Psalm\Plugin\Hook\FunctionReturnTypeProviderInterface` + - `Psalm\Plugin\Hook\FunctionExistenceProviderInterface` + - `Psalm\Plugin\Hook\AfterAnalysisInterface` + - `Psalm\Plugin\Hook\MethodVisibilityProviderInterface` + - `Psalm\Plugin\Hook\MethodParamsProviderInterface` + - `Psalm\Plugin\Hook\AfterClassLikeExistenceCheckInterface` + - `Psalm\Plugin\Hook\PropertyTypeProviderInterface` + - `Psalm\Plugin\Hook\AfterFunctionCallAnalysisInterface` + - `Psalm\Plugin\Hook\MethodExistenceProviderInterface` + - `Psalm\Plugin\Hook\AfterCodebasePopulatedInterface` + - `Psalm\Plugin\Hook\AfterClassLikeAnalysisInterface` + - `Psalm\Plugin\Hook\PropertyVisibilityProviderInterface` + - `Psalm\Plugin\Hook\AfterStatementAnalysisInterface` From ba69f4fb31affc7089162ab5188bb2f08111f543 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 19:14:58 +0200 Subject: [PATCH 29/78] Dropped deprecated `CodeIssue` methods --- src/Psalm/Issue/CodeIssue.php | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/Psalm/Issue/CodeIssue.php b/src/Psalm/Issue/CodeIssue.php index 00bb8b2fef9..c6d55215edd 100644 --- a/src/Psalm/Issue/CodeIssue.php +++ b/src/Psalm/Issue/CodeIssue.php @@ -38,15 +38,6 @@ public function __construct( $this->message = $message; } - /** - * @deprecated going to be removed in Psalm 5 - * @psalm-suppress PossiblyUnusedMethod - */ - public function getLocation(): CodeLocation - { - return $this->code_location; - } - public function getShortLocationWithPrevious(): string { $previous_text = ''; @@ -69,24 +60,6 @@ public function getFilePath(): string return $this->code_location->file_path; } - /** - * @deprecated going to be removed in Psalm 5 - * @psalm-suppress PossiblyUnusedMethod for convenience - */ - public function getFileName(): string - { - return $this->code_location->file_name; - } - - /** - * @deprecated going to be removed in Psalm 5 - * @psalm-suppress PossiblyUnusedMethod - */ - public function getMessage(): string - { - return $this->message; - } - public static function getIssueType(): string { $fqcn_parts = explode('\\', static::class); From 0866866348ab2cad63d9920d135f677418b4bd45 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 19:28:23 +0200 Subject: [PATCH 30/78] Document CodeIssue methods removal --- UPGRADING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UPGRADING.md b/UPGRADING.md index 8b804f27960..0a5e60f03ac 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -152,3 +152,6 @@ - `Psalm\Plugin\Hook\AfterClassLikeAnalysisInterface` - `Psalm\Plugin\Hook\PropertyVisibilityProviderInterface` - `Psalm\Plugin\Hook\AfterStatementAnalysisInterface` + - [BC] Method `Psalm\Issue\CodeIssue::getLocation()` was removed + - [BC] Method `Psalm\Issue\CodeIssue::getFileName()` was removed + - [BC] Method `Psalm\Issue\CodeIssue::getMessage()` was removed From 401c2e9a836c371022de6c410276ddb9da16d963 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 19:48:43 +0200 Subject: [PATCH 31/78] Dropped deprecated TypeAnalyzer methods --- src/Psalm/Internal/Analyzer/TypeAnalyzer.php | 55 -------------------- 1 file changed, 55 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/TypeAnalyzer.php b/src/Psalm/Internal/Analyzer/TypeAnalyzer.php index 2a8451c1e02..9d1648c5961 100644 --- a/src/Psalm/Internal/Analyzer/TypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/TypeAnalyzer.php @@ -2,12 +2,7 @@ namespace Psalm\Internal\Analyzer; -use Psalm\Codebase; -use Psalm\Internal\Type\Comparator\AtomicTypeComparator; -use Psalm\Internal\Type\Comparator\TypeComparisonResult; -use Psalm\Internal\Type\Comparator\UnionTypeComparator; use Psalm\Type; -use Psalm\Type\Atomic; use Psalm\Type\Union; use function array_keys; @@ -19,56 +14,6 @@ */ class TypeAnalyzer { - /** - * Does the input param type match the given param type - * - * @deprecated in favour of UnionTypeComparator, going to be removed in Psalm 5 - * @psalm-suppress PossiblyUnusedMethod - */ - public static function isContainedBy( - Codebase $codebase, - Union $input_type, - Union $container_type, - bool $ignore_null = false, - bool $ignore_false = false, - ?TypeComparisonResult $union_comparison_result = null, - bool $allow_interface_equality = false - ): bool { - return UnionTypeComparator::isContainedBy( - $codebase, - $input_type, - $container_type, - $ignore_null, - $ignore_false, - $union_comparison_result, - $allow_interface_equality - ); - } - - /** - * Does the input param atomic type match the given param atomic type - * - * @deprecated in favour of AtomicTypeComparator, going to be removed in Psalm 5 - * @psalm-suppress PossiblyUnusedMethod - */ - public static function isAtomicContainedBy( - Codebase $codebase, - Atomic $input_type_part, - Atomic $container_type_part, - bool $allow_interface_equality = false, - bool $allow_float_int_equality = true, - ?TypeComparisonResult $atomic_comparison_result = null - ): bool { - return AtomicTypeComparator::isContainedBy( - $codebase, - $input_type_part, - $container_type_part, - $allow_interface_equality, - $allow_float_int_equality, - $atomic_comparison_result - ); - } - /** * Takes two arrays of types and merges them * From e04d545328ab961944f76dc9177908ea6479cd6d Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 20:00:56 +0200 Subject: [PATCH 32/78] Dropped deprecated `DocComment` methods --- src/Psalm/DocComment.php | 144 --------------------------------------- 1 file changed, 144 deletions(-) diff --git a/src/Psalm/DocComment.php b/src/Psalm/DocComment.php index ae3cd6ca32a..d1c0bc51dd0 100644 --- a/src/Psalm/DocComment.php +++ b/src/Psalm/DocComment.php @@ -7,25 +7,15 @@ use Psalm\Internal\Scanner\DocblockParser; use Psalm\Internal\Scanner\ParsedDocblock; -use function array_filter; use function explode; -use function implode; use function in_array; -use function min; use function preg_match; -use function preg_match_all; -use function preg_replace; -use function rtrim; -use function str_repeat; -use function str_replace; use function strlen; use function strpos; use function strspn; use function substr; use function trim; -use const PREG_SET_ORDER; - class DocComment { public const PSALM_ANNOTATIONS = [ @@ -45,140 +35,6 @@ class DocComment 'consistent-templates', 'if-this-is', 'this-out' ]; - /** - * Parse a docblock comment into its parts. - * - * Taken from advanced api docmaker, which was taken from - * https://github.com/facebook/libphutil/blob/master/src/parser/docblock/PhutilDocblockParser.php - * - * @return array Array of the main comment and specials - * - * @psalm-return array{description:string, specials:array>} - * @psalm-suppress PossiblyUnusedMethod - * - * @deprecated use parsePreservingLength instead, going to be removed in Psalm 5 - * - * @psalm-pure - */ - public static function parse(string $docblock, ?int $line_number = null, bool $preserve_format = false): array - { - // Strip off comments. - $docblock = trim($docblock); - $docblock = preg_replace('@^/\*\*@', '', $docblock); - $docblock = preg_replace('@\*/$@', '', $docblock); - $docblock = preg_replace('@^[ \t]*\*@m', '', $docblock); - - // Normalize multi-line @specials. - $lines = explode("\n", $docblock); - - $line_map = []; - - $last = false; - foreach ($lines as $k => $line) { - if (preg_match('/^\s?@\w/i', $line)) { - $last = $k; - } elseif (preg_match('/^\s*$/', $line)) { - $last = false; - } elseif ($last !== false) { - $old_last_line = $lines[$last]; - $lines[$last] = rtrim($old_last_line) - . ($preserve_format || trim($old_last_line) === '@return' ? "\n" . $line : ' ' . trim($line)); - - if ($line_number) { - $old_line_number = $line_map[$old_last_line]; - unset($line_map[$old_last_line]); - $line_map[$lines[$last]] = $old_line_number; - } - - unset($lines[$k]); - } - - if ($line_number) { - $line_map[$line] = $line_number++; - } - } - - $special = []; - - if ($preserve_format) { - foreach ($lines as $m => $line) { - if (preg_match('/^\s?@([\w\-:]+)[\t ]*(.*)$/sm', $line, $matches)) { - [$full_match, $type, $data] = $matches; - - $docblock = str_replace($full_match, '', $docblock); - - if (empty($special[$type])) { - $special[$type] = []; - } - - $line_number = $line_map && isset($line_map[$full_match]) ? $line_map[$full_match] : $m; - - $special[$type][$line_number] = rtrim($data); - } - } - } else { - $docblock = implode("\n", $lines); - - // Parse @specials. - if (preg_match_all('/^\s?@([\w\-:]+)[\t ]*([^\n]*)/m', $docblock, $matches, PREG_SET_ORDER)) { - $docblock = preg_replace('/^\s?@([\w\-:]+)\s*([^\n]*)/m', '', $docblock); - foreach ($matches as $m => $match) { - [$_, $type, $data] = $match; - - if (empty($special[$type])) { - $special[$type] = []; - } - - $line_number = $line_map && isset($line_map[$_]) ? $line_map[$_] : $m; - - $special[$type][$line_number] = $data; - } - } - } - - $docblock = str_replace("\t", ' ', $docblock); - - // Smush the whole docblock to the left edge. - $min_indent = 80; - $indent = 0; - foreach (array_filter(explode("\n", $docblock)) as $line) { - for ($ii = 0, $iiMax = strlen($line); $ii < $iiMax; ++$ii) { - if ($line[$ii] !== ' ') { - break; - } - ++$indent; - } - - $min_indent = min($indent, $min_indent); - } - - $docblock = preg_replace('/^' . str_repeat(' ', $min_indent) . '/m', '', $docblock); - $docblock = rtrim($docblock); - - // Trim any empty lines off the front, but leave the indent level if there - // is one. - $docblock = preg_replace('/^\s*\n/', '', $docblock); - - foreach ($special as $special_key => $_) { - if (strpos($special_key, 'psalm-') === 0) { - $special_key = substr($special_key, 6); - - if (!in_array( - $special_key, - self::PSALM_ANNOTATIONS, - true - )) { - throw new DocblockParseException('Unrecognised annotation @psalm-' . $special_key); - } - } - } - - return [ - 'description' => $docblock, - 'specials' => $special, - ]; - } - /** * Parse a docblock comment into its parts. */ From 804d0c651d72449d23d0fd23b63f2773f72ad04f Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 20:03:52 +0200 Subject: [PATCH 33/78] Documented DocComment methods removal --- UPGRADING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/UPGRADING.md b/UPGRADING.md index 0a5e60f03ac..15e991fd278 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -155,3 +155,4 @@ - [BC] Method `Psalm\Issue\CodeIssue::getLocation()` was removed - [BC] Method `Psalm\Issue\CodeIssue::getFileName()` was removed - [BC] Method `Psalm\Issue\CodeIssue::getMessage()` was removed + - [BC] Method `Psalm\DocComment::parse()` was removed From 988ae9496aa946b2d163e6c987a7c8fe8f4cc1d8 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 20:33:01 +0200 Subject: [PATCH 34/78] Trim baseline selection Refs vimeo/psalm#6000 --- src/Psalm/ErrorBaseline.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Psalm/ErrorBaseline.php b/src/Psalm/ErrorBaseline.php index 9759ab944c5..41235a317bc 100644 --- a/src/Psalm/ErrorBaseline.php +++ b/src/Psalm/ErrorBaseline.php @@ -282,12 +282,7 @@ function (string $extension): string { foreach ($existingIssueType['s'] as $selection) { $codeNode = $baselineDoc->createElement('code'); - - /** @todo in major version release (e.g. Psalm 5) replace $selection with trim($selection) - * This will be a minor BC break as baselines generated will then not be compatible with Psalm - * versions from before PR https://github.com/vimeo/psalm/pull/6000 - */ - $codeNode->textContent = $selection; + $codeNode->textContent = trim($selection); $issueNode->appendChild($codeNode); } $fileNode->appendChild($issueNode); From dbee1188125a8ea459ebc0c88caadd171c2d0bae Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 20:46:24 +0200 Subject: [PATCH 35/78] Moved `getPsalmHelpText()` to `Cli\Psalm` --- src/Psalm/Internal/Cli/Psalm.php | 167 ++++++++++++++++++++++++++++++- src/Psalm/Internal/CliUtils.php | 165 ------------------------------ 2 files changed, 166 insertions(+), 166 deletions(-) diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 6bf2c607fa8..96b8c9b984b 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -192,7 +192,7 @@ public static function run(array $argv): void if (array_key_exists('h', $options)) { - echo CliUtils::getPsalmHelpText(); + echo self::getHelpText(); /* --shepherd[=host] Send data to Shepherd, Psalm's GitHub integration tool. @@ -1173,4 +1173,169 @@ private static function generateStubs( ); } } + + /** + * @psalm-pure + */ + private static function getHelpText(): string + { + return << Date: Mon, 3 Jan 2022 20:54:22 +0200 Subject: [PATCH 36/78] Dropped `html-escaped-string` docs --- docs/annotating_code/type_syntax/scalar_types.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/annotating_code/type_syntax/scalar_types.md b/docs/annotating_code/type_syntax/scalar_types.md index ff99acc7e2a..c326001db8c 100644 --- a/docs/annotating_code/type_syntax/scalar_types.md +++ b/docs/annotating_code/type_syntax/scalar_types.md @@ -73,9 +73,3 @@ Strings that don't pass this type check: A non empty string, lowercased or both at once. `empty` here is defined as all strings except the empty string `''`. Another type `non-falsy-string` is effectively a subtype of `non-empty-string`, and also precludes the string value `'0'`. - -### html-escaped-string (deprecated) - -A string which can safely be used in a html context. - -_This type will be removed in Psalm 5.x._ From afa64970516efb314c131136973298fd464a7426 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 22:37:41 +0200 Subject: [PATCH 37/78] Dropped `THtmlEscapedString` --- UPGRADING.md | 1 - .../plugins/plugins_type_system.md | 2 +- .../echo-checker/EchoChecker.php | 11 ++--- psalm-baseline.xml | 3 -- .../Type/Comparator/ScalarTypeComparator.php | 7 +-- src/Psalm/Internal/Type/TypeTokenizer.php | 3 +- src/Psalm/Type/Atomic.php | 4 -- src/Psalm/Type/Atomic/THtmlEscapedString.php | 25 ---------- tests/Config/PluginTest.php | 49 ------------------- 9 files changed, 9 insertions(+), 96 deletions(-) delete mode 100644 src/Psalm/Type/Atomic/THtmlEscapedString.php diff --git a/UPGRADING.md b/UPGRADING.md index 15e991fd278..d43930e25d0 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -36,7 +36,6 @@ - `Psalm\Type\Atomic\TEnumCase` - `Psalm\Type\Atomic\TFalse` - `Psalm\Type\Atomic\TGenericObject` - - `Psalm\Type\Atomic\THtmlEscapedString` - `Psalm\Type\Atomic\TIntMask` - `Psalm\Type\Atomic\TIntMaskOf` - `Psalm\Type\Atomic\TIntRange` diff --git a/docs/running_psalm/plugins/plugins_type_system.md b/docs/running_psalm/plugins/plugins_type_system.md index 311bf37df56..031cb16ad09 100644 --- a/docs/running_psalm/plugins/plugins_type_system.md +++ b/docs/running_psalm/plugins/plugins_type_system.md @@ -139,7 +139,7 @@ if (true === $first) { `TCallableString` - denotes the `callable-string` type, used to represent an unknown string that is also `callable`. -`THtmlEscapedString`, `TSqlSelectString` - these are special types, specifically for consumption by plugins. +`TSqlSelectString` - this is a special type, specifically for consumption by plugins. `TLowercaseString` - denotes a string where every character is lowercased. (which can also result from a `strtolower` call). diff --git a/examples/plugins/composer-based/echo-checker/EchoChecker.php b/examples/plugins/composer-based/echo-checker/EchoChecker.php index c75eac76a56..a7255e4fd4a 100644 --- a/examples/plugins/composer-based/echo-checker/EchoChecker.php +++ b/examples/plugins/composer-based/echo-checker/EchoChecker.php @@ -1,16 +1,15 @@ getStmt(); $statements_source = $event->getStatementsSource(); if ($stmt instanceof PhpParser\Node\Stmt\Echo_) { @@ -46,7 +46,6 @@ public static function afterStatementAnalysis(AfterStatementAnalysisEvent $event foreach ($types as $type) { if ($type instanceof TString && !$type instanceof TLiteralString - && !$type instanceof THtmlEscapedString ) { if (IssueBuffer::accepts( new ArgumentTypeCoercion( diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 2c30710e499..85b6ca14f2b 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -353,9 +353,6 @@ - - new THtmlEscapedString() - array_keys($template_type_map[$value])[0] diff --git a/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php index a8fdce09fc3..c387835375e 100644 --- a/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php @@ -16,7 +16,6 @@ use Psalm\Type\Atomic\TDependentListKey; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; -use Psalm\Type\Atomic\THtmlEscapedString; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TLiteralClassString; @@ -536,8 +535,7 @@ public static function isContainedBy( } if ($container_type_part instanceof TString - && ($input_type_part instanceof TNumericString - || $input_type_part instanceof THtmlEscapedString) + && $input_type_part instanceof TNumericString ) { if ($container_type_part instanceof TLiteralString) { if (is_numeric($container_type_part->value) && $atomic_comparison_result) { @@ -551,8 +549,7 @@ public static function isContainedBy( } if ($input_type_part instanceof TString - && ($container_type_part instanceof TNumericString - || $container_type_part instanceof THtmlEscapedString) + && $container_type_part instanceof TNumericString ) { if ($input_type_part instanceof TLiteralString) { return is_numeric($input_type_part->value); diff --git a/src/Psalm/Internal/Type/TypeTokenizer.php b/src/Psalm/Internal/Type/TypeTokenizer.php index 8230990b978..846062c7fc3 100644 --- a/src/Psalm/Internal/Type/TypeTokenizer.php +++ b/src/Psalm/Internal/Type/TypeTokenizer.php @@ -54,8 +54,7 @@ class TypeTokenizer 'stringable-object' => true, 'pure-callable' => true, 'pure-Closure' => true, - 'mysql-escaped-string' => true, // deprecated - 'html-escaped-string' => true, // deprecated + 'mysql-escaped-string' => true, // deprecated, should be removed in Psalm 5 'literal-string' => true, 'non-empty-literal-string' => true, 'lowercase-string' => true, diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 03dbc2eb7a2..e548df58219 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -32,7 +32,6 @@ use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TGenericObject; -use Psalm\Type\Atomic\THtmlEscapedString; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TIterable; @@ -269,9 +268,6 @@ public static function create( case 'numeric-string': return new TNumericString(); - case 'html-escaped-string': - return new THtmlEscapedString(); - case 'literal-string': return new TNonspecificLiteralString(); diff --git a/src/Psalm/Type/Atomic/THtmlEscapedString.php b/src/Psalm/Type/Atomic/THtmlEscapedString.php deleted file mode 100644 index 6a291a84136..00000000000 --- a/src/Psalm/Type/Atomic/THtmlEscapedString.php +++ /dev/null @@ -1,25 +0,0 @@ -getKey(); - } - - public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool - { - return false; - } -} diff --git a/tests/Config/PluginTest.php b/tests/Config/PluginTest.php index 096d51bcdc8..2476d9be4ea 100644 --- a/tests/Config/PluginTest.php +++ b/tests/Config/PluginTest.php @@ -293,55 +293,6 @@ public function testEchoAnalyzerPluginWithUnescapedString(): void $this->analyzeFile($file_path, new Context()); } - public function testEchoAnalyzerPluginWithEscapedString(): void - { - $this->project_analyzer = $this->getProjectAnalyzerWithConfig( - TestConfig::loadFromXML( - dirname(__DIR__, 2) . DIRECTORY_SEPARATOR, - ' - - - - - - - - - - - - ' - ) - ); - - $this->project_analyzer->getCodebase()->config->initializePlugins($this->project_analyzer); - - $file_path = getcwd() . '/src/somefile.php'; - - $this->addFile( - $file_path, - ' - Some text - ' - ); - - $this->analyzeFile($file_path, new Context()); - } - public function testFileAnalyzerPlugin(): void { require_once __DIR__ . '/Plugin/FilePlugin.php'; From f09814ac178d8e8124480ce2fdded0e9f4583426 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 3 Jan 2022 22:44:45 +0200 Subject: [PATCH 38/78] Documented THtmlEscapedString removal --- UPGRADING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UPGRADING.md b/UPGRADING.md index d43930e25d0..70bf8292114 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -155,3 +155,6 @@ - [BC] Method `Psalm\Issue\CodeIssue::getFileName()` was removed - [BC] Method `Psalm\Issue\CodeIssue::getMessage()` was removed - [BC] Method `Psalm\DocComment::parse()` was removed + - [BC] Class `Psalm\Type\Atomic\THtmlEscapedString` has been removed + + From 30013cb81aa98fe202ab10ecccff99886f29a73d Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 4 Jan 2022 10:13:44 +0200 Subject: [PATCH 39/78] Drop orphaned token Refs vimeo/psalm#7285 --- src/Psalm/Internal/Type/TypeTokenizer.php | 1 - tests/TypeComparatorTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Psalm/Internal/Type/TypeTokenizer.php b/src/Psalm/Internal/Type/TypeTokenizer.php index 846062c7fc3..a3e96874d3c 100644 --- a/src/Psalm/Internal/Type/TypeTokenizer.php +++ b/src/Psalm/Internal/Type/TypeTokenizer.php @@ -54,7 +54,6 @@ class TypeTokenizer 'stringable-object' => true, 'pure-callable' => true, 'pure-Closure' => true, - 'mysql-escaped-string' => true, // deprecated, should be removed in Psalm 5 'literal-string' => true, 'non-empty-literal-string' => true, 'lowercase-string' => true, diff --git a/tests/TypeComparatorTest.php b/tests/TypeComparatorTest.php index 1092cdfb11f..4496e8fa94f 100644 --- a/tests/TypeComparatorTest.php +++ b/tests/TypeComparatorTest.php @@ -73,7 +73,6 @@ public function getAllBasicTypes(): array $basic_generic_types, [ 'open-resource' => true, // unverifiable - 'mysql-escaped-string' => true, // deprecated 'non-empty-countable' => true, // bit weird, maybe a bug? ] ); From fa33632958f44c4b2eba668a7da1e156370e4f10 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 4 Jan 2022 10:25:00 +0200 Subject: [PATCH 40/78] Bump PHP version to 7.4 Also exclude 8.2+ for now - we don't know if current Psalm version would be compatible with that. --- .github/workflows/ci.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ae030d4f46..b9b7d2edcfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.1' + php-version: '7.4' tools: composer:v2 coverage: none diff --git a/composer.json b/composer.json index 67e1687a34e..db1cd2a84db 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ } ], "require": { - "php": "^7.1|^8", + "php": "^7.4 || ~8.0.0 || ~8.1.0", "ext-SimpleXML": "*", "ext-ctype": "*", "ext-dom": "*", From 20567ff72067c50686d97def7365542af202802f Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 4 Jan 2022 11:29:00 +0200 Subject: [PATCH 41/78] Remove the polyfill we no longer need --- composer.json | 5 +--- psalm.xml.dist | 1 - src/spl_object_id.php | 54 ------------------------------------------- 3 files changed, 1 insertion(+), 59 deletions(-) delete mode 100644 src/spl_object_id.php diff --git a/composer.json b/composer.json index db1cd2a84db..93fd17b9f01 100644 --- a/composer.json +++ b/composer.json @@ -80,10 +80,7 @@ "autoload": { "psr-4": { "Psalm\\": "src/Psalm/" - }, - "files": [ - "src/spl_object_id.php" - ] + } }, "autoload-dev": { "psr-4": { diff --git a/psalm.xml.dist b/psalm.xml.dist index 0343b7cb1fc..c1ddfa58bf3 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -34,7 +34,6 @@ - diff --git a/src/spl_object_id.php b/src/spl_object_id.php deleted file mode 100644 index 8b30da94ab3..00000000000 --- a/src/spl_object_id.php +++ /dev/null @@ -1,54 +0,0 @@ -isUserDefined()) { - /** - * See https://github.com/runkit7/runkit_object_id for a faster native version for php <= 7.1 - * - * @param object $object - * @return int The object id - */ - function spl_object_id($object): int - { - return runkit_object_id($object); - } - } elseif (PHP_INT_SIZE === 8) { - /** - * See https://github.com/runkit7/runkit_object_id for a faster native version for php <= 7.1 - * - * @param object $object - * @return int (The object id, XORed with a random number) - */ - function spl_object_id($object): int - { - $hash = spl_object_hash($object); - // Fit this into a php long (32-bit or 64-bit signed int). - // The first 16 hex digits (64 bytes) vary, the last 16 don't. - // Values are usually padded with 0s at the front. - return intval(substr($hash, 1, 15), 16); - } - } else { - /** - * See https://github.com/runkit7/runkit_object_id for a faster native version for php <= 7.1 - * - * @param object $object - * @return int (The object id, XORed with a random number) - */ - function spl_object_id($object): int - { - $hash = spl_object_hash($object); - // Fit this into a php long (32-bit or 64-bit signed int). - // The first 16 hex digits (64 bytes) vary, the last 16 don't. - // Values are usually padded with 0s at the front. - return intval(substr($hash, 9, 7), 16); - } - } -} From cf962eebc2a1fe39e939321c4efa136411875939 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 4 Jan 2022 11:40:55 +0200 Subject: [PATCH 42/78] Promote conditional tests to always-running --- tests/ClosureTest.php | 16 ++++++++-------- tests/FunctionCallTest.php | 12 ++++++------ tests/Traits/InvalidCodeAnalysisTestTrait.php | 6 +----- tests/Traits/ValidCodeAnalysisTestTrait.php | 14 +------------- tests/TypeReconciliation/ConditionalTest.php | 2 +- 5 files changed, 17 insertions(+), 33 deletions(-) diff --git a/tests/ClosureTest.php b/tests/ClosureTest.php index 64a17a53d28..a9291d88923 100644 --- a/tests/ClosureTest.php +++ b/tests/ClosureTest.php @@ -330,7 +330,7 @@ public function callTheInvokableIndirectly(): bool { } }', ], - 'PHP71-mirrorCallableParams' => [ + 'mirrorCallableParams' => [ ' [ + 'closureFromCallableInvokableNamedClass' => [ ' [ + 'closureFromCallableInvokableAnonymousClass' => [ ' [ + 'publicCallableFromInside' => [ ' [ + 'protectedCallableFromInside' => [ ' [ + 'closureFromCallableNamedFunction' => [ ' 'TypeDoesNotContainType', ], - 'PHP71-closureFromCallableInvokableNamedClassWrongArgs' => [ + 'closureFromCallableInvokableNamedClassWrongArgs' => [ ' 'DuplicateParam', ], - 'PHP71-privateCallable' => [ + 'privateCallable' => [ ' 'int', ], ], - 'PHP73-hrtime' => [ + 'hrtime' => [ ' 'array{0: int, 1: int}', ], ], - 'PHP73-hrtimeCanBeFloat' => [ + 'hrtimeCanBeFloat' => [ ' [ + 'allowIsCountableToInformType' => [ ' 'array', ], ], - 'PHP72-pregMatchWithFlagUnmatchedAsNull' => [ + 'pregMatchWithFlagUnmatchedAsNull' => [ ' [ @@ -1353,7 +1353,7 @@ function takesInt(int $i) : void {} '$matches===' => 'array', ], ], - 'PHP72-pregMatchWithFlagOffsetCaptureAndUnmatchedAsNull' => [ + 'pregMatchWithFlagOffsetCaptureAndUnmatchedAsNull' => [ ' [ @@ -1399,7 +1399,7 @@ function foo(int $a, string $b, bool $c) : array { return compact("a", "b", "c"); }', ], - 'PHP73-setCookiePhp73' => [ + 'setCookiePhp73' => [ 'getTestName(); - if (strpos($test_name, 'PHP71-') !== false) { - if (version_compare(PHP_VERSION, '7.1.0', '<')) { - $this->markTestSkipped('Test case requires PHP 7.1.'); - } - } elseif (strpos($test_name, 'PHP80-') !== false) { + if (strpos($test_name, 'PHP80-') !== false) { if (version_compare(PHP_VERSION, '8.0.0', '<')) { $this->markTestSkipped('Test case requires PHP 8.0.'); } diff --git a/tests/Traits/ValidCodeAnalysisTestTrait.php b/tests/Traits/ValidCodeAnalysisTestTrait.php index 46a57ac4a22..0bef93d31b8 100644 --- a/tests/Traits/ValidCodeAnalysisTestTrait.php +++ b/tests/Traits/ValidCodeAnalysisTestTrait.php @@ -39,19 +39,7 @@ public function testValidCode( string $php_version = '7.3' ): void { $test_name = $this->getTestName(); - if (strpos($test_name, 'PHP71-') !== false) { - if (version_compare(PHP_VERSION, '7.1.0', '<')) { - $this->markTestSkipped('Test case requires PHP 7.1.'); - } - } elseif (strpos($test_name, 'PHP72-') !== false) { - if (version_compare(PHP_VERSION, '7.2.0', '<')) { - $this->markTestSkipped('Test case requires PHP 7.2.'); - } - } elseif (strpos($test_name, 'PHP73-') !== false) { - if (version_compare(PHP_VERSION, '7.3.0', '<')) { - $this->markTestSkipped('Test case requires PHP 7.3.'); - } - } elseif (strpos($test_name, 'PHP80-') !== false) { + if (strpos($test_name, 'PHP80-') !== false) { if (version_compare(PHP_VERSION, '8.0.0', '<')) { $this->markTestSkipped('Test case requires PHP 8.0.'); } diff --git a/tests/TypeReconciliation/ConditionalTest.php b/tests/TypeReconciliation/ConditionalTest.php index 670c3bb138f..a786b2ca64c 100644 --- a/tests/TypeReconciliation/ConditionalTest.php +++ b/tests/TypeReconciliation/ConditionalTest.php @@ -1006,7 +1006,7 @@ function f() { return rand(0,1) ? "f" : 1.1; } atan($a); atan($b);', ], - 'PHP71-removeNonCallable' => [ + 'removeNonCallable' => [ ' Date: Tue, 4 Jan 2022 12:43:12 +0200 Subject: [PATCH 43/78] Tighten dependency ranges --- composer.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 93fd17b9f01..6cc57bf5284 100644 --- a/composer.json +++ b/composer.json @@ -28,12 +28,12 @@ "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^1.1 || ^2.0 || ^3.0", "dnoegel/php-xdg-base-dir": "^0.1.1", - "felixfbecker/advanced-json-rpc": "^3.0.3", + "felixfbecker/advanced-json-rpc": "^3.1", "felixfbecker/language-server-protocol": "^1.5", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", "nikic/php-parser": "^4.13", "openlss/lib-array2xml": "^1.0", - "sebastian/diff": "^3.0 || ^4.0", + "sebastian/diff": "^4.0", "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0", "webmozart/path-util": "^2.3" }, @@ -42,16 +42,16 @@ }, "require-dev": { "ext-curl": "*", - "bamarni/composer-bin-plugin": "^1.2", + "bamarni/composer-bin-plugin": "^1.4", "brianium/paratest": "^4.0||^6.0", "php-parallel-lint/php-parallel-lint": "^1.2", "phpdocumentor/reflection-docblock": "^5", "phpmyadmin/sql-parser": "5.1.0||dev-master", - "phpspec/prophecy": ">=1.9.0", + "phpspec/prophecy": ">=1.10.2", "phpunit/phpunit": "^9.0", "psalm/plugin-phpunit": "^0.16.1", "slevomat/coding-standard": "^7.0", - "squizlabs/php_codesniffer": "^3.5", + "squizlabs/php_codesniffer": "^3.6", "symfony/process": "^4.3 || ^5.0 || ^6.0", "weirdan/prophecy-shim": "^1.0 || ^2.0" }, From 02b91cc54eca5cd1d7f4c8782fa1c5cdd3455972 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 4 Jan 2022 15:15:34 +0200 Subject: [PATCH 44/78] Replaced deprecated webmozart/path-util --- composer.json | 2 +- src/Psalm/Config.php | 2 +- src/Psalm/Internal/Cli/Psalm.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 6cc57bf5284..9643f0892b3 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "openlss/lib-array2xml": "^1.0", "sebastian/diff": "^4.0", "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0", - "webmozart/path-util": "^2.3" + "symfony/filesystem": "^5.4 || ^6.0" }, "provide": { "psalm/psalm": "self.version" diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index ad27a3e237a..a24c9c952e6 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -38,9 +38,9 @@ use Psalm\Progress\VoidProgress; use SimpleXMLElement; use SimpleXMLIterator; +use Symfony\Component\Filesystem\Path; use Throwable; use UnexpectedValueException; -use Webmozart\PathUtil\Path; use XdgBaseDir\Xdg; use stdClass; diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 96b8c9b984b..ee170e5c1b7 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -33,7 +33,7 @@ use Psalm\Report; use Psalm\Report\ReportOptions; use RuntimeException; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_filter; use function array_key_exists; From 2c6d4f86876818d036b54e09a7f4c8e5569acdb6 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Tue, 4 Jan 2022 16:04:36 +0000 Subject: [PATCH 45/78] Fix erroneous intersection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found during a line-for-line port. We’re using this value in two different places, and it only makes sense to use it in one --- src/Psalm/Internal/Type/AssertionReconciler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 9f0cbe6b3f2..46628551954 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -386,7 +386,6 @@ private static function refine( if ($existing_var_type_part instanceof TNamedObject || $existing_var_type_part instanceof TTemplateParam ) { - $new_type_part->addIntersectionType($existing_var_type_part); $acceptable_atomic_types[] = clone $existing_var_type_part; } else { if (AtomicTypeComparator::isContainedBy( From 0fffb55a83f200d42916e030d7139bf83cc0b36f Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Tue, 4 Jan 2022 16:08:06 +0000 Subject: [PATCH 46/78] Simplify some logic around negations to prevent unecessary looping --- .../Type/NegatedAssertionReconciler.php | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index eb763bba427..df90d2a95ed 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -176,39 +176,37 @@ public static function reconcile( // if there wasn't a direct hit, go deeper, eliminating subtypes if (!$existing_var_type->removeType($assertion)) { - foreach ($existing_var_type->getAtomicTypes() as $part_name => $existing_var_type_part) { - if (!$existing_var_type_part->isObjectType() || strpos($assertion, '-')) { - continue; - } - + if (!strpos($assertion, '-')) { $assertion_type = Type::parseString($assertion, null, $template_type_map); - if (!$assertion_type->isSingle()) { - continue; - } - - $new_type_part = $assertion_type->getSingleAtomic(); - - if (!$new_type_part instanceof TNamedObject) { - continue; - } - - if (AtomicTypeComparator::isContainedBy( - $codebase, - $existing_var_type_part, - $new_type_part, - false, - false - )) { - $existing_var_type->removeType($part_name); - } elseif (AtomicTypeComparator::isContainedBy( - $codebase, - $new_type_part, - $existing_var_type_part, - false, - false - )) { - $existing_var_type->different = true; + if ($assertion_type->isSingle()) { + $new_type_part = $assertion_type->getSingleAtomic(); + + if ($new_type_part instanceof TNamedObject) { + foreach ($existing_var_type->getAtomicTypes() as $part_name => $existing_var_type_part) { + if (!$existing_var_type_part->isObjectType()) { + continue; + } + + if (AtomicTypeComparator::isContainedBy( + $codebase, + $existing_var_type_part, + $new_type_part, + false, + false + )) { + $existing_var_type->removeType($part_name); + } elseif (AtomicTypeComparator::isContainedBy( + $codebase, + $new_type_part, + $existing_var_type_part, + false, + false + )) { + $existing_var_type->different = true; + } + } + } } } } From 4e81d0c5ab78820f56da220e17fcee3dedbe143c Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Tue, 4 Jan 2022 16:36:33 +0000 Subject: [PATCH 47/78] Remove an untested chunk of issue-triggering code that has only ever caused anguish --- .../Internal/Type/AssertionReconciler.php | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 46628551954..a29565832ef 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -473,30 +473,6 @@ private static function refine( } elseif (!$new_type->hasMixed()) { $has_match = true; - if ($key - && $code_location - && $new_type->getId() === $existing_var_type->getId() - //even if two objects are the same, equality is not guaranteed - // example: (ErrorException and TypeError are both Throwable but not equal) - && !$new_type->hasNamedObjectType() - && !$is_equality - && !($original_assertion === 'loaded-class-string' && $old_var_type_string === 'class-string') - && (!($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer) - || ($key !== '$this' - && !($existing_var_type->hasLiteralClassString() && $new_type->hasLiteralClassString()))) - ) { - self::triggerIssueForImpossible( - $existing_var_type, - $old_var_type_string, - $key, - $original_assertion, - true, - $negated, - $code_location, - $suppressed_issues - ); - } - $any_scalar_type_match_found = false; if ($code_location From 63f3460df74a4268c51c4cd426466b10d8b79c96 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Tue, 4 Jan 2022 17:08:34 +0000 Subject: [PATCH 48/78] Make clear that class string check for trait $this key only applies to certain assertions --- src/Psalm/Internal/Type/AssertionReconciler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index a29565832ef..b7a1ef9a079 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -482,7 +482,7 @@ private static function refine( && !$new_type_has_interface && (!($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer) || ($key !== '$this' - && !($existing_var_type->hasLiteralClassString() && $new_type->hasLiteralClassString()))) + && strpos($original_assertion, 'isa-') !== 0)) && UnionTypeComparator::isContainedBy( $codebase, $existing_var_type, From 0c13d8e719ba5b9dc7a6c71b597baa090a891ad5 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 4 Jan 2022 19:57:41 +0200 Subject: [PATCH 49/78] Indent heredoc PHP 7.3 allows indenting of heredoc / nowdoc strings. --- src/Psalm/Internal/Cli/LanguageServer.php | 62 +++--- src/Psalm/Internal/Cli/Psalm.php | 220 +++++++++++----------- src/Psalm/Internal/Cli/Psalter.php | 78 ++++---- src/Psalm/Internal/Cli/Refactor.php | 44 ++--- 4 files changed, 202 insertions(+), 202 deletions(-) diff --git a/src/Psalm/Internal/Cli/LanguageServer.php b/src/Psalm/Internal/Cli/LanguageServer.php index 94a0d2ad549..8abf04bcab5 100644 --- a/src/Psalm/Internal/Cli/LanguageServer.php +++ b/src/Psalm/Internal/Cli/LanguageServer.php @@ -143,51 +143,51 @@ function (string $arg) use ($valid_long_options): void { if (array_key_exists('h', $options)) { echo << Date: Wed, 5 Jan 2022 01:00:05 +0200 Subject: [PATCH 50/78] Apply literal number separator rector I opted for customary 3-digit groups, except version id, where 2-digit groups are used to match the version id encoding. --- examples/TemplateScanner.php | 2 +- src/Psalm/Codebase.php | 4 +-- src/Psalm/Config.php | 10 +++---- src/Psalm/Internal/Algebra.php | 12 ++++----- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 2 +- .../FunctionLike/ReturnTypeAnalyzer.php | 2 +- .../Analyzer/FunctionLikeAnalyzer.php | 2 +- .../Internal/Analyzer/MethodComparator.php | 10 +++---- .../Internal/Analyzer/ProjectAnalyzer.php | 2 +- .../Block/IfElse/ElseIfAnalyzer.php | 2 +- .../Statements/Expression/ArrayAnalyzer.php | 4 +-- .../Expression/Call/ArgumentAnalyzer.php | 10 ++++--- .../Statements/Expression/CallAnalyzer.php | 4 +-- .../Statements/Expression/CastAnalyzer.php | 2 +- .../Statements/ExpressionAnalyzer.php | 6 ++--- src/Psalm/Internal/Cli/LanguageServer.php | 2 +- src/Psalm/Internal/Cli/Psalm.php | 2 +- src/Psalm/Internal/Cli/Psalter.php | 4 +-- src/Psalm/Internal/CliUtils.php | 6 ++--- src/Psalm/Internal/Fork/Pool.php | 4 +-- .../Reflector/FunctionLikeNodeScanner.php | 2 +- .../Internal/PhpVisitor/ReflectorVisitor.php | 4 +-- .../Internal/Provider/StatementsProvider.php | 6 ++--- .../Stubs/Generator/StubsGenerator.php | 2 +- .../Type/Comparator/AtomicTypeComparator.php | 2 +- src/Psalm/IssueBuffer.php | 4 +-- src/Psalm/PluginRegistrationSocket.php | 8 +++--- src/Psalm/Progress/DefaultProgress.php | 2 +- src/Psalm/Type/Atomic.php | 14 +++++----- .../Type/Atomic/TAnonymousClassInstance.php | 2 +- src/Psalm/Type/Atomic/TBool.php | 2 +- src/Psalm/Type/Atomic/TCallableObject.php | 2 +- src/Psalm/Type/Atomic/TFloat.php | 2 +- src/Psalm/Type/Atomic/TGenericObject.php | 2 +- src/Psalm/Type/Atomic/TInt.php | 2 +- src/Psalm/Type/Atomic/TIterable.php | 2 +- src/Psalm/Type/Atomic/TMixed.php | 4 +-- src/Psalm/Type/Atomic/TNamedObject.php | 8 +++--- src/Psalm/Type/Atomic/TObject.php | 2 +- src/Psalm/Type/Atomic/TString.php | 2 +- src/Psalm/Type/Atomic/TVoid.php | 2 +- src/Psalm/Type/Union.php | 10 +++---- tests/AlgebraTest.php | 4 +-- .../GetMemoryLimitInBytesTest.php | 26 +++++++++---------- tests/Config/ConfigTest.php | 8 +++--- tests/EndToEnd/PsalmEndToEndTest.php | 2 +- tests/FileDiffTest.php | 12 ++++----- 47 files changed, 116 insertions(+), 114 deletions(-) diff --git a/examples/TemplateScanner.php b/examples/TemplateScanner.php index 4775758e325..2ed2caae02f 100644 --- a/examples/TemplateScanner.php +++ b/examples/TemplateScanner.php @@ -25,7 +25,7 @@ public function scan( ): void { $stmts = $codebase->statements_provider->getStatementsForFile( $file_storage->file_path, - 70400, + 7_04_00, $progress ); diff --git a/src/Psalm/Codebase.php b/src/Psalm/Codebase.php index de0878130e0..7ac7bba4925 100644 --- a/src/Psalm/Codebase.php +++ b/src/Psalm/Codebase.php @@ -1999,12 +1999,12 @@ public function addTaintSink( public function getMinorAnalysisPhpVersion(): int { - return self::transformPhpVersionId($this->analysis_php_version_id % 10000, 100); + return self::transformPhpVersionId($this->analysis_php_version_id % 10_000, 100); } public function getMajorAnalysisPhpVersion(): int { - return self::transformPhpVersionId($this->analysis_php_version_id, 10000); + return self::transformPhpVersionId($this->analysis_php_version_id, 10_000); } public static function transformPhpVersionId(int $php_version_id, int $div): int diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index a24c9c952e6..4e2978649cd 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -522,7 +522,7 @@ class Config /** * @var int */ - public $max_string_length = 1000; + public $max_string_length = 1_000; /** @var ?IncludeCollector */ private $include_collector; @@ -1865,7 +1865,7 @@ public function visitPreloadedStubFiles(Codebase $codebase, ?Progress $progress $core_generic_files = []; - if (PHP_VERSION_ID < 80000 && $codebase->analysis_php_version_id >= 80000) { + if (PHP_VERSION_ID < 8_00_00 && $codebase->analysis_php_version_id >= 8_00_00) { $stringable_path = dirname(__DIR__, 2) . '/stubs/Php80.phpstub'; if (!file_exists($stringable_path)) { @@ -1875,7 +1875,7 @@ public function visitPreloadedStubFiles(Codebase $codebase, ?Progress $progress $core_generic_files[] = $stringable_path; } - if (PHP_VERSION_ID < 80100 && $codebase->analysis_php_version_id >= 80100) { + if (PHP_VERSION_ID < 8_01_00 && $codebase->analysis_php_version_id >= 8_01_00) { $stringable_path = dirname(__DIR__, 2) . '/stubs/Php81.phpstub'; if (!file_exists($stringable_path)) { @@ -1926,12 +1926,12 @@ public function visitStubFiles(Codebase $codebase, ?Progress $progress = null): $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'SPL.phpstub', ]; - if (PHP_VERSION_ID >= 80000 && $codebase->analysis_php_version_id >= 80000) { + if (PHP_VERSION_ID >= 8_00_00 && $codebase->analysis_php_version_id >= 8_00_00) { $stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php80.phpstub'; $this->internal_stubs[] = $stringable_path; } - if (PHP_VERSION_ID >= 80100 && $codebase->analysis_php_version_id >= 80100) { + if (PHP_VERSION_ID >= 8_01_00 && $codebase->analysis_php_version_id >= 8_01_00) { $stringable_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Php81.phpstub'; $this->internal_stubs[] = $stringable_path; } diff --git a/src/Psalm/Internal/Algebra.php b/src/Psalm/Internal/Algebra.php index 8f6b08c4dee..a622de7d437 100644 --- a/src/Psalm/Internal/Algebra.php +++ b/src/Psalm/Internal/Algebra.php @@ -99,7 +99,7 @@ public static function simplifyCNF(array $clauses): array //65536 seems to be a significant threshold, when put at 65537, the code https://psalm.dev/r/216f362ea6 goes //from seconds in analysis to many minutes - if ($clause_count > 65536) { + if ($clause_count > 65_536) { return []; } @@ -443,7 +443,7 @@ public static function groupImpossibilities(array $clauses): array ++$complexity; - if ($complexity > 20000) { + if ($complexity > 20_000) { throw new ComplicatedExpressionException(); } } @@ -469,7 +469,7 @@ public static function combineOredClauses( array $right_clauses, int $conditional_object_id ): array { - if (count($left_clauses) > 60000 || count($right_clauses) > 60000) { + if (count($left_clauses) > 60_000 || count($right_clauses) > 60_000) { return []; } @@ -601,7 +601,7 @@ function ($clause) { ); if (!$clauses) { - $cond_id = mt_rand(0, 100000000); + $cond_id = mt_rand(0, 100_000_000); return [new Clause([], $cond_id, $cond_id, true)]; } @@ -616,14 +616,14 @@ function ($clause) { $impossible_clauses = self::groupImpossibilities($clauses_with_impossibilities); if (!$impossible_clauses) { - $cond_id = mt_rand(0, 100000000); + $cond_id = mt_rand(0, 100_000_000); return [new Clause([], $cond_id, $cond_id, true)]; } $negated = self::simplifyCNF($impossible_clauses); if (!$negated) { - $cond_id = mt_rand(0, 100000000); + $cond_id = mt_rand(0, 100_000_000); return [new Clause([], $cond_id, $cond_id, true)]; } diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 97ec0ed11f5..0726a3ea3e9 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1608,7 +1608,7 @@ private static function addOrUpdatePropertyType( $codebase = $project_analyzer->getCodebase(); $allow_native_type = !$docblock_only - && $codebase->analysis_php_version_id >= 70400 + && $codebase->analysis_php_version_id >= 7_04_00 && $codebase->allow_backwards_incompatible_changes; $manipulator->setType( diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php index a7fd0690f15..1512bf9d64f 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php @@ -940,7 +940,7 @@ private static function addOrUpdateReturnType( } $allow_native_type = !$docblock_only - && $codebase->analysis_php_version_id >= 70000 + && $codebase->analysis_php_version_id >= 7_00_00 && ( $codebase->allow_backwards_incompatible_changes || $is_final diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 864c3c81087..afce62eaac8 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -1442,7 +1442,7 @@ public function addOrUpdateParamType( } $allow_native_type = !$docblock_only - && $codebase->analysis_php_version_id >= 70000 + && $codebase->analysis_php_version_id >= 7_00_00 && ( $codebase->allow_backwards_incompatible_changes || $is_final diff --git a/src/Psalm/Internal/Analyzer/MethodComparator.php b/src/Psalm/Internal/Analyzer/MethodComparator.php index beb533876d7..0de16d9ef99 100644 --- a/src/Psalm/Internal/Analyzer/MethodComparator.php +++ b/src/Psalm/Internal/Analyzer/MethodComparator.php @@ -95,7 +95,7 @@ public static function compare( $cased_implementer_method_id, $prevent_method_signature_mismatch, $prevent_abstract_override, - $codebase->analysis_php_version_id >= 80000, + $codebase->analysis_php_version_id >= 8_00_00, $code_location, $suppressed_issues ); @@ -562,7 +562,7 @@ private static function compareMethodSignatureParams( $implementer_classlike_storage->parent_class ); - $is_contained_by = $codebase->analysis_php_version_id >= 70400 + $is_contained_by = $codebase->analysis_php_version_id >= 7_04_00 && $guide_param_signature_type ? UnionTypeComparator::isContainedBy( $codebase, @@ -576,7 +576,7 @@ private static function compareMethodSignatureParams( if (!$is_contained_by) { $config = Config::getInstance(); - if ($codebase->analysis_php_version_id >= 80000 + if ($codebase->analysis_php_version_id >= 8_00_00 || $guide_classlike_storage->is_trait === $implementer_classlike_storage->is_trait || !in_array($guide_classlike_storage->name, $implementer_classlike_storage->used_traits) || $implementer_method_storage->defining_fqcln !== $implementer_classlike_storage->name @@ -854,7 +854,7 @@ private static function compareMethodSignatureReturnTypes( $implementer_classlike_storage->parent_class ) : null; - $is_contained_by = $codebase->analysis_php_version_id >= 70400 + $is_contained_by = $codebase->analysis_php_version_id >= 7_04_00 && $implementer_signature_return_type ? UnionTypeComparator::isContainedBy( $codebase, @@ -864,7 +864,7 @@ private static function compareMethodSignatureReturnTypes( : UnionTypeComparator::isContainedByInPhp($implementer_signature_return_type, $guide_signature_return_type); if (!$is_contained_by) { - if ($codebase->analysis_php_version_id >= 80000 + if ($codebase->analysis_php_version_id >= 8_00_00 || $guide_classlike_storage->is_trait === $implementer_classlike_storage->is_trait || !in_array($guide_classlike_storage->name, $implementer_classlike_storage->used_traits) || $implementer_method_storage->defining_fqcln !== $implementer_classlike_storage->name diff --git a/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php b/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php index abe4d6e562b..fe3b80318df 100644 --- a/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php @@ -1297,7 +1297,7 @@ public function setPhpVersion(string $version, string $source): void $php_major_version = (int) $php_major_version; $php_minor_version = (int) $php_minor_version; - $analysis_php_version_id = $php_major_version * 10000 + $php_minor_version * 100; + $analysis_php_version_id = $php_major_version * 10_000 + $php_minor_version * 100; if ($this->codebase->analysis_php_version_id !== $analysis_php_version_id) { // reset lexer and parser when php version changes diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php index 26f0c3f2670..0f95381a6b7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php @@ -341,7 +341,7 @@ function (array $carry, Clause $clause): array { $reasonable_clause_count = count($if_scope->reasonable_clauses); - if ($reasonable_clause_count && $reasonable_clause_count < 20000 && $elseif_clauses) { + if ($reasonable_clause_count && $reasonable_clause_count < 20_000 && $elseif_clauses) { $if_scope->reasonable_clauses = Algebra::combineOredClauses( $if_scope->reasonable_clauses, $elseif_clauses, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index 98dc159f6fe..a3f7b191afc 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -510,7 +510,7 @@ private static function handleUnpackedArray( if ($unpacked_atomic_type instanceof TKeyedArray) { foreach ($unpacked_atomic_type->properties as $key => $property_value) { if (is_string($key)) { - if ($codebase->analysis_php_version_id <= 80000) { + if ($codebase->analysis_php_version_id <= 8_00_00) { IssueBuffer::maybeAdd( new DuplicateArrayKey( 'String keys are not supported in unpacked arrays', @@ -553,7 +553,7 @@ private static function handleUnpackedArray( $array_creation_info->can_create_objectlike = false; if ($unpacked_atomic_type->type_params[0]->hasString()) { - if ($codebase->analysis_php_version_id <= 80000) { + if ($codebase->analysis_php_version_id <= 8_00_00) { IssueBuffer::maybeAdd( new DuplicateArrayKey( 'String keys are not supported in unpacked arrays', diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 40e510735c7..bc82d57d329 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -486,7 +486,7 @@ private static function checkFunctionLikeTypeMatches( if ($function_param->is_variadic) { $arg_type = $unpacked_atomic_array->getGenericValueType(); - } elseif ($codebase->analysis_php_version_id >= 80000 + } elseif ($codebase->analysis_php_version_id >= 8_00_00 && $allow_named_args && isset($unpacked_atomic_array->properties[$function_param->name]) ) { @@ -552,7 +552,9 @@ private static function checkFunctionLikeTypeMatches( continue; } - if (($codebase->analysis_php_version_id < 80000 || !$allow_named_args) && !$key_type->isInt()) { + if (($codebase->analysis_php_version_id < 8_00_00 || !$allow_named_args) + && !$key_type->isInt() + ) { $invalid_string_key = true; continue; @@ -578,7 +580,7 @@ private static function checkFunctionLikeTypeMatches( 'Method ' . $cased_method_id . ' called with unpacked iterable ' . $arg_type->getId() . ' with invalid key (must be ' - . ($codebase->analysis_php_version_id < 80000 ? 'int' : 'int|string') . ')', + . ($codebase->analysis_php_version_id < 8_00_00 ? 'int' : 'int|string') . ')', new CodeLocation($statements_analyzer->getSource(), $arg->value), $cased_method_id ), @@ -586,7 +588,7 @@ private static function checkFunctionLikeTypeMatches( ); } if ($invalid_string_key) { - if ($codebase->analysis_php_version_id < 80000) { + if ($codebase->analysis_php_version_id < 8_00_00) { IssueBuffer::maybeAdd( new $issue_type( 'String keys not supported in unpacked arguments', diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index b40ec32f97d..851172f3aca 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -831,8 +831,8 @@ public static function applyAssertionsToContext( ); $assert_clauses = FormulaGenerator::getFormula( - mt_rand(0, 1000000), - mt_rand(0, 1000000), + mt_rand(0, 1_000_000), + mt_rand(0, 1_000_000), $conditional, $context->self, $statements_analyzer, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index e8b67fbb49d..3d34b3f44f2 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -264,7 +264,7 @@ public static function analyze( } if ($stmt instanceof PhpParser\Node\Expr\Cast\Unset_ - && $statements_analyzer->getCodebase()->analysis_php_version_id <= 70400 + && $statements_analyzer->getCodebase()->analysis_php_version_id <= 7_04_00 ) { if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) { return false; diff --git a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php index bbb020fab50..552a3ecaedf 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php @@ -473,17 +473,17 @@ private static function handleExpression( $codebase = $statements_analyzer->getCodebase(); $analysis_php_version_id = $codebase->analysis_php_version_id; - if ($stmt instanceof PhpParser\Node\Expr\Match_ && $analysis_php_version_id >= 80000) { + if ($stmt instanceof PhpParser\Node\Expr\Match_ && $analysis_php_version_id >= 8_00_00) { return MatchAnalyzer::analyze($statements_analyzer, $stmt, $context); } - if ($stmt instanceof PhpParser\Node\Expr\Throw_ && $analysis_php_version_id >= 80000) { + if ($stmt instanceof PhpParser\Node\Expr\Throw_ && $analysis_php_version_id >= 8_00_00) { return ThrowAnalyzer::analyze($statements_analyzer, $stmt, $context); } if (($stmt instanceof PhpParser\Node\Expr\NullsafePropertyFetch || $stmt instanceof PhpParser\Node\Expr\NullsafeMethodCall) - && $analysis_php_version_id >= 80000 + && $analysis_php_version_id >= 8_00_00 ) { return NullsafeAnalyzer::analyze($statements_analyzer, $stmt, $context); } diff --git a/src/Psalm/Internal/Cli/LanguageServer.php b/src/Psalm/Internal/Cli/LanguageServer.php index 94a0d2ad549..0d5a5b91dbc 100644 --- a/src/Psalm/Internal/Cli/LanguageServer.php +++ b/src/Psalm/Internal/Cli/LanguageServer.php @@ -121,7 +121,7 @@ function (string $arg) use ($valid_long_options): void { if (!array_key_exists('use-ini-defaults', $options)) { ini_set('display_errors', '1'); ini_set('display_startup_errors', '1'); - ini_set('memory_limit', (string) (8 * 1024 * 1024 * 1024)); + ini_set('memory_limit', (string) (8 * 1_024 * 1_024 * 1_024)); } if (array_key_exists('help', $options)) { diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index ee170e5c1b7..44f8abfff14 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -475,7 +475,7 @@ private static function setMemoryLimit(array $options): void ini_set('display_errors', 'stderr'); ini_set('display_startup_errors', '1'); - $memoryLimit = (8 * 1024 * 1024 * 1024); + $memoryLimit = (8 * 1_024 * 1_024 * 1_024); if (array_key_exists('memory-limit', $options)) { $memoryLimit = $options['memory-limit']; diff --git a/src/Psalm/Internal/Cli/Psalter.php b/src/Psalm/Internal/Cli/Psalter.php index d2765a2bd46..4172dd6131f 100644 --- a/src/Psalm/Internal/Cli/Psalter.php +++ b/src/Psalm/Internal/Cli/Psalter.php @@ -424,8 +424,8 @@ private static function setMemoryLimit(): void { $memLimit = CliUtils::getMemoryLimitInBytes(); // Magic number is 4096M in bytes - if ($memLimit > 0 && $memLimit < 8 * 1024 * 1024 * 1024) { - ini_set('memory_limit', (string) (8 * 1024 * 1024 * 1024)); + if ($memLimit > 0 && $memLimit < 8 * 1_024 * 1_024 * 1_024) { + ini_set('memory_limit', (string) (8 * 1_024 * 1_024 * 1_024)); } } diff --git a/src/Psalm/Internal/CliUtils.php b/src/Psalm/Internal/CliUtils.php index 74b8d34b40c..37b7782be66 100644 --- a/src/Psalm/Internal/CliUtils.php +++ b/src/Psalm/Internal/CliUtils.php @@ -456,13 +456,13 @@ public static function convertMemoryLimitToBytes(string $limit): int $limit = (int)$matches[1]; switch (strtoupper($matches[2] ?? '')) { case 'G': - $limit *= 1024 * 1024 * 1024; + $limit *= 1_024 * 1_024 * 1_024; break; case 'M': - $limit *= 1024 * 1024; + $limit *= 1_024 * 1_024; break; case 'K': - $limit *= 1024; + $limit *= 1_024; break; } } diff --git a/src/Psalm/Internal/Fork/Pool.php b/src/Psalm/Internal/Fork/Pool.php index 25113319f52..e97604a68b5 100644 --- a/src/Psalm/Internal/Fork/Pool.php +++ b/src/Psalm/Internal/Fork/Pool.php @@ -266,7 +266,7 @@ public function __construct( if ($bytes_written < $bytes_to_write) { // wait a bit - usleep(500000); + usleep(500_000); } } @@ -368,7 +368,7 @@ private function readResultsFromChildren(): array // For each stream that was ready, read the content. foreach ($needs_read as $file) { - $buffer = fread($file, 1024); + $buffer = fread($file, 1_024); if ($buffer !== false) { $content[(int)$file] .= $buffer; } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index 9d4c218cbda..5abb9b7cc98 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -1077,7 +1077,7 @@ private function createStorageForFunctionLike( if ($method_name_lc === strtolower($class_name) && !isset($classlike_storage->methods['__construct']) && strpos($fq_classlike_name, '\\') === false - && $this->codebase->analysis_php_version_id <= 70400 + && $this->codebase->analysis_php_version_id <= 7_04_00 ) { $this->codebase->methods->setDeclaringMethodId( $fq_classlike_name, diff --git a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php index 1dccc6dc6f5..85fa7351967 100644 --- a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php @@ -237,7 +237,7 @@ public function enterNode(PhpParser\Node $node): ?int $this->functionlike_node_scanners[] = $functionlike_node_scanner; if ($classlike_storage - && $this->codebase->analysis_php_version_id >= 80000 + && $this->codebase->analysis_php_version_id >= 8_00_00 && $node instanceof PhpParser\Node\Stmt\ClassMethod && strtolower($node->name->name) === '__tostring' ) { @@ -247,7 +247,7 @@ public function enterNode(PhpParser\Node $node): ?int $classlike_storage->class_implements['stringable'] = 'Stringable'; } - if (PHP_VERSION_ID >= 80000) { + if (PHP_VERSION_ID >= 8_00_00) { $this->codebase->scanner->queueClassLikeForScanning('Stringable'); } } diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index 92ab2887653..b157026799c 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -180,7 +180,7 @@ public function getStatementsForFile( if ($existing_statements && $existing_file_contents - && abs(strlen($existing_file_contents) - strlen($file_contents)) < 5000 + && abs(strlen($existing_file_contents) - strlen($file_contents)) < 5_000 ) { $file_changes = FileDiffer::getDiff($existing_file_contents, $file_contents); @@ -433,8 +433,8 @@ public static function parseStatements( ]; if (!self::$lexer) { - $major_version = Codebase::transformPhpVersionId($analysis_php_version_id, 10000); - $minor_version = Codebase::transformPhpVersionId($analysis_php_version_id % 10000, 100); + $major_version = Codebase::transformPhpVersionId($analysis_php_version_id, 10_000); + $minor_version = Codebase::transformPhpVersionId($analysis_php_version_id % 10_000, 100); self::$lexer = new PhpParser\Lexer\Emulative([ 'usedAttributes' => $attributes, 'phpVersion' => $major_version . '.' . $minor_version, diff --git a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php index c9888361380..9504c12ff74 100644 --- a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php @@ -386,7 +386,7 @@ public static function getParserTypeFromPsalmType(Union $type): ?PhpParser\NodeA || $atomic_type instanceof TArray || $atomic_type instanceof TIterable ) { - $identifier_string = $atomic_type->toPhpString(null, [], null, 80000); + $identifier_string = $atomic_type->toPhpString(null, [], null, 8_00_00); if ($identifier_string === null) { throw new UnexpectedValueException( diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index ed209cacdf5..881d34c2dc7 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -508,7 +508,7 @@ public static function isContainedBy( if ($input_type_part instanceof TNamedObject) { // check whether the object has a __toString method if ($codebase->classOrInterfaceExists($input_type_part->value)) { - if ($codebase->analysis_php_version_id >= 80000 + if ($codebase->analysis_php_version_id >= 8_00_00 && ($input_type_part->value === 'Stringable' || ($codebase->classlikes->classExists($input_type_part->value) && $codebase->classlikes->classImplements($input_type_part->value, 'Stringable')) diff --git a/src/Psalm/IssueBuffer.php b/src/Psalm/IssueBuffer.php index 45632b51023..49defb85280 100644 --- a/src/Psalm/IssueBuffer.php +++ b/src/Psalm/IssueBuffer.php @@ -726,7 +726,7 @@ function (IssueData $d1, IssueData $d2): int { if ($start_time) { echo 'Checks took ' . number_format(microtime(true) - $start_time, 2) . ' seconds'; - echo ' and used ' . number_format(memory_get_peak_usage() / (1024 * 1024), 3) . 'MB of memory' . "\n"; + echo ' and used ' . number_format(memory_get_peak_usage() / (1_024 * 1_024), 3) . 'MB of memory' . "\n"; $analysis_summary = $codebase->analyzer->getTypeInferenceSummary($codebase); echo $analysis_summary . "\n"; @@ -753,7 +753,7 @@ function (IssueData $d1, IssueData $d2): int { break; } - echo $function_id . ': ' . round(1000 * $time, 2) . 'ms per node' . "\n"; + echo $function_id . ': ' . round(1_000 * $time, 2) . 'ms per node' . "\n"; } echo "\n"; diff --git a/src/Psalm/PluginRegistrationSocket.php b/src/Psalm/PluginRegistrationSocket.php index f9f85268cfc..6a24935ee93 100644 --- a/src/Psalm/PluginRegistrationSocket.php +++ b/src/Psalm/PluginRegistrationSocket.php @@ -123,7 +123,7 @@ public function addFileTypeScanner(string $fileExtension, string $className): vo $className, FileScanner::class ), - 1622727271 + 1_622_727_271 ); } if (!empty($this->config->getFiletypeScanners()[$fileExtension]) @@ -131,7 +131,7 @@ public function addFileTypeScanner(string $fileExtension, string $className): vo ) { throw new LogicException( sprintf('Cannot redeclare scanner for file-type %s', $fileExtension), - 1622727272 + 1_622_727_272 ); } $this->additionalFileTypeScanners[$fileExtension] = $className; @@ -159,7 +159,7 @@ public function addFileTypeAnalyzer(string $fileExtension, string $className): v $className, FileAnalyzer::class ), - 1622727281 + 1_622_727_281 ); } if (!empty($this->config->getFiletypeAnalyzers()[$fileExtension]) @@ -167,7 +167,7 @@ public function addFileTypeAnalyzer(string $fileExtension, string $className): v ) { throw new LogicException( sprintf('Cannot redeclare analyzer for file-type %s', $fileExtension), - 1622727282 + 1_622_727_282 ); } $this->additionalFileTypeAnalyzers[$fileExtension] = $className; diff --git a/src/Psalm/Progress/DefaultProgress.php b/src/Psalm/Progress/DefaultProgress.php index 76f91e98297..2bacfb0758f 100644 --- a/src/Psalm/Progress/DefaultProgress.php +++ b/src/Psalm/Progress/DefaultProgress.php @@ -9,7 +9,7 @@ class DefaultProgress extends LongProgress { - private const TOO_MANY_FILES = 1500; + private const TOO_MANY_FILES = 1_500; // Update the progress bar at most once per 0.1 seconds. // This reduces flickering and reduces the amount of time spent writing to STDERR and updating the terminal. diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index e548df58219..1655de15f12 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -135,7 +135,7 @@ public static function create( return new TBool(); case 'void': - if ($analysis_php_version_id === null || $analysis_php_version_id >= 70100) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 7_01_00) { return new TVoid(); } @@ -145,14 +145,14 @@ public static function create( return new TArrayKey(); case 'iterable': - if ($analysis_php_version_id === null || $analysis_php_version_id >= 70100) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 7_01_00) { return new TIterable(); } break; case 'never': - if ($analysis_php_version_id === null || $analysis_php_version_id >= 80100) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 8_01_00) { return new TNever(); } @@ -164,7 +164,7 @@ public static function create( return new TNever(); case 'object': - if ($analysis_php_version_id === null || $analysis_php_version_id >= 70200) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 7_02_00) { return new TObject(); } @@ -223,7 +223,7 @@ public static function create( return $analysis_php_version_id !== null ? new TNamedObject($value) : new TTrue(); case 'false': - if ($analysis_php_version_id === null || $analysis_php_version_id >= 80000) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 8_00_00) { return new TFalse(); } @@ -236,14 +236,14 @@ public static function create( return $analysis_php_version_id !== null ? new TNamedObject($value) : new TScalar(); case 'null': - if ($analysis_php_version_id === null || $analysis_php_version_id >= 80000) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 8_00_00) { return new TNull(); } return new TNamedObject($value); case 'mixed': - if ($analysis_php_version_id === null || $analysis_php_version_id >= 80000) { + if ($analysis_php_version_id === null || $analysis_php_version_id >= 8_00_00) { return new TMixed(); } diff --git a/src/Psalm/Type/Atomic/TAnonymousClassInstance.php b/src/Psalm/Type/Atomic/TAnonymousClassInstance.php index 57cda6dc01d..83189316e4e 100644 --- a/src/Psalm/Type/Atomic/TAnonymousClassInstance.php +++ b/src/Psalm/Type/Atomic/TAnonymousClassInstance.php @@ -28,7 +28,7 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 70200 ? ($this->extends ?? 'object') : null; + return $analysis_php_version_id >= 7_02_00 ? ($this->extends ?? 'object') : null; } /** diff --git a/src/Psalm/Type/Atomic/TBool.php b/src/Psalm/Type/Atomic/TBool.php index bdd48fb463d..936085be193 100644 --- a/src/Psalm/Type/Atomic/TBool.php +++ b/src/Psalm/Type/Atomic/TBool.php @@ -26,6 +26,6 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 70000 ? 'bool' : null; + return $analysis_php_version_id >= 7_00_00 ? 'bool' : null; } } diff --git a/src/Psalm/Type/Atomic/TCallableObject.php b/src/Psalm/Type/Atomic/TCallableObject.php index c210976beec..a10ea30526e 100644 --- a/src/Psalm/Type/Atomic/TCallableObject.php +++ b/src/Psalm/Type/Atomic/TCallableObject.php @@ -26,7 +26,7 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 72000 ? 'object' : null; + return $analysis_php_version_id >= 7_20_00 ? 'object' : null; } public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool diff --git a/src/Psalm/Type/Atomic/TFloat.php b/src/Psalm/Type/Atomic/TFloat.php index d5ac7ea901b..0acdbff07d0 100644 --- a/src/Psalm/Type/Atomic/TFloat.php +++ b/src/Psalm/Type/Atomic/TFloat.php @@ -26,6 +26,6 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 70000 ? 'float' : null; + return $analysis_php_version_id >= 7_00_00 ? 'float' : null; } } diff --git a/src/Psalm/Type/Atomic/TGenericObject.php b/src/Psalm/Type/Atomic/TGenericObject.php index 79bff4589a0..8b05040a4b9 100644 --- a/src/Psalm/Type/Atomic/TGenericObject.php +++ b/src/Psalm/Type/Atomic/TGenericObject.php @@ -73,7 +73,7 @@ public function toPhpString( ): ?string { $result = $this->toNamespacedString($namespace, $aliased_classes, $this_class, true); $intersection = strrpos($result, '&'); - if ($intersection === false || $analysis_php_version_id >= 80100) { + if ($intersection === false || $analysis_php_version_id >= 8_01_00) { return $result; } return substr($result, $intersection+1); diff --git a/src/Psalm/Type/Atomic/TInt.php b/src/Psalm/Type/Atomic/TInt.php index c40c6d23244..5cdccd63a38 100644 --- a/src/Psalm/Type/Atomic/TInt.php +++ b/src/Psalm/Type/Atomic/TInt.php @@ -26,6 +26,6 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 70000 ? 'int' : null; + return $analysis_php_version_id >= 7_00_00 ? 'int' : null; } } diff --git a/src/Psalm/Type/Atomic/TIterable.php b/src/Psalm/Type/Atomic/TIterable.php index 010dcd2aac7..c0c1970a614 100644 --- a/src/Psalm/Type/Atomic/TIterable.php +++ b/src/Psalm/Type/Atomic/TIterable.php @@ -91,7 +91,7 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 70100 ? 'iterable' : null; + return $analysis_php_version_id >= 7_01_00 ? 'iterable' : null; } public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool diff --git a/src/Psalm/Type/Atomic/TMixed.php b/src/Psalm/Type/Atomic/TMixed.php index bbb21545d2f..b31dfe21e72 100644 --- a/src/Psalm/Type/Atomic/TMixed.php +++ b/src/Psalm/Type/Atomic/TMixed.php @@ -36,12 +36,12 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 80000 ? 'mixed' : null; + return $analysis_php_version_id >= 8_00_00 ? 'mixed' : null; } public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { - return $analysis_php_version_id >= 80000; + return $analysis_php_version_id >= 8_00_00; } public function getAssertionString(bool $exact = false): string diff --git a/src/Psalm/Type/Atomic/TNamedObject.php b/src/Psalm/Type/Atomic/TNamedObject.php index 1e5113fbe29..2ad72a83233 100644 --- a/src/Psalm/Type/Atomic/TNamedObject.php +++ b/src/Psalm/Type/Atomic/TNamedObject.php @@ -121,16 +121,16 @@ public function toPhpString( int $analysis_php_version_id ): ?string { if ($this->value === 'static') { - return $analysis_php_version_id >= 80000 ? 'static' : null; + return $analysis_php_version_id >= 8_00_00 ? 'static' : null; } if ($this->was_static && $this->value === $this_class) { - return $analysis_php_version_id >= 80000 ? 'static' : 'self'; + return $analysis_php_version_id >= 8_00_00 ? 'static' : 'self'; } $result = $this->toNamespacedString($namespace, $aliased_classes, $this_class, false); $intersection = strrpos($result, '&'); - if ($intersection === false || $analysis_php_version_id >= 80100) { + if ($intersection === false || $analysis_php_version_id >= 8_01_00) { return $result; } return substr($result, $intersection+1); @@ -138,7 +138,7 @@ public function toPhpString( public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { - return ($this->value !== 'static' && $this->was_static === false) || $analysis_php_version_id >= 80000; + return ($this->value !== 'static' && $this->was_static === false) || $analysis_php_version_id >= 8_00_00; } public function replaceTemplateTypesWithArgTypes( diff --git a/src/Psalm/Type/Atomic/TObject.php b/src/Psalm/Type/Atomic/TObject.php index c7cc67fff5b..5b8e81743ed 100644 --- a/src/Psalm/Type/Atomic/TObject.php +++ b/src/Psalm/Type/Atomic/TObject.php @@ -28,7 +28,7 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 70200 ? $this->getKey() : null; + return $analysis_php_version_id >= 7_02_00 ? $this->getKey() : null; } public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool diff --git a/src/Psalm/Type/Atomic/TString.php b/src/Psalm/Type/Atomic/TString.php index 986d585b85a..88ddebd0a6f 100644 --- a/src/Psalm/Type/Atomic/TString.php +++ b/src/Psalm/Type/Atomic/TString.php @@ -16,7 +16,7 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 70000 ? 'string' : null; + return $analysis_php_version_id >= 7_00_00 ? 'string' : null; } public function __toString(): string diff --git a/src/Psalm/Type/Atomic/TVoid.php b/src/Psalm/Type/Atomic/TVoid.php index 4241c5e0818..9c5eb290f9c 100644 --- a/src/Psalm/Type/Atomic/TVoid.php +++ b/src/Psalm/Type/Atomic/TVoid.php @@ -28,7 +28,7 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 70100 ? $this->getKey() : null; + return $analysis_php_version_id >= 7_01_00 ? $this->getKey() : null; } public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 843fd742dd7..9d864da44c2 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -516,11 +516,11 @@ public function toPhpString( int $analysis_php_version_id ): ?string { if (!$this->isSingleAndMaybeNullable()) { - if ($analysis_php_version_id < 80000) { + if ($analysis_php_version_id < 8_00_00) { return null; } - } elseif ($analysis_php_version_id < 70000 - || (isset($this->types['null']) && $analysis_php_version_id < 70100) + } elseif ($analysis_php_version_id < 7_00_00 + || (isset($this->types['null']) && $analysis_php_version_id < 7_01_00) ) { return null; } @@ -569,7 +569,7 @@ public function toPhpString( return implode('|', array_unique($php_types)); } - if ($analysis_php_version_id < 80000) { + if ($analysis_php_version_id < 8_00_00) { return ($nullable ? '?' : '') . implode('|', array_unique($php_types)); } if ($nullable) { @@ -580,7 +580,7 @@ public function toPhpString( public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { - if (!$this->isSingleAndMaybeNullable() && $analysis_php_version_id < 80000) { + if (!$this->isSingleAndMaybeNullable() && $analysis_php_version_id < 8_00_00) { return false; } diff --git a/tests/AlgebraTest.php b/tests/AlgebraTest.php index 406e66816d6..401029f2aa4 100644 --- a/tests/AlgebraTest.php +++ b/tests/AlgebraTest.php @@ -85,7 +85,7 @@ public function testCombinatorialExpansion(): void $has_errors = false; - $dnf_stmt = StatementsProvider::parseStatements($dnf, 70400, $has_errors)[0]; + $dnf_stmt = StatementsProvider::parseStatements($dnf, 7_04_00, $has_errors)[0]; $this->assertInstanceOf(PhpParser\Node\Stmt\Expression::class, $dnf_stmt); @@ -101,7 +101,7 @@ public function testCombinatorialExpansion(): void $statements_analyzer ); - $this->assertCount(6561, $dnf_clauses); + $this->assertCount(6_561, $dnf_clauses); $simplified_dnf_clauses = Algebra::simplifyCNF($dnf_clauses); diff --git a/tests/CommandFunctions/GetMemoryLimitInBytesTest.php b/tests/CommandFunctions/GetMemoryLimitInBytesTest.php index d2dee3f6ec0..b842143df06 100644 --- a/tests/CommandFunctions/GetMemoryLimitInBytesTest.php +++ b/tests/CommandFunctions/GetMemoryLimitInBytesTest.php @@ -18,21 +18,21 @@ public function memoryLimitSettingProvider(): array // byte values [1, 1], [512, 512], - [2048, 2048], + [2_048, 2_048], // uppercase units - ['1K', 1024], - ['24K', 24576], - ['1M', 1048576], - ['24M', 25165824], - ['1G', 1073741824], - ['24G', 25769803776], + ['1K', 1_024], + ['24K', 24_576], + ['1M', 1_048_576], + ['24M', 25_165_824], + ['1G', 1_073_741_824], + ['24G', 25_769_803_776], // lowercase units - ['1k', 1024], - ['24k', 24576], - ['1m', 1048576], - ['24m', 25165824], - ['1g', 1073741824], - ['24g', 25769803776], + ['1k', 1_024], + ['24k', 24_576], + ['1m', 1_048_576], + ['24m', 25_165_824], + ['1g', 1_073_741_824], + ['24g', 25_769_803_776], ]; } diff --git a/tests/Config/ConfigTest.php b/tests/Config/ConfigTest.php index ab00b72775b..2ec4fcfac9d 100644 --- a/tests/Config/ConfigTest.php +++ b/tests/Config/ConfigTest.php @@ -1353,10 +1353,10 @@ public function pluginRegistersScannerAndAnalyzerDataProvider(): array { return [ 'regular' => [0, null], // flags, expected exception code - 'invalid scanner class' => [FileTypeSelfRegisteringPlugin::FLAG_SCANNER_INVALID, 1622727271], - 'invalid analyzer class' => [FileTypeSelfRegisteringPlugin::FLAG_ANALYZER_INVALID, 1622727281], - 'override scanner' => [FileTypeSelfRegisteringPlugin::FLAG_SCANNER_TWICE, 1622727272], - 'override analyzer' => [FileTypeSelfRegisteringPlugin::FLAG_ANALYZER_TWICE, 1622727282], + 'invalid scanner class' => [FileTypeSelfRegisteringPlugin::FLAG_SCANNER_INVALID, 1_622_727_271], + 'invalid analyzer class' => [FileTypeSelfRegisteringPlugin::FLAG_ANALYZER_INVALID, 1_622_727_281], + 'override scanner' => [FileTypeSelfRegisteringPlugin::FLAG_SCANNER_TWICE, 1_622_727_272], + 'override analyzer' => [FileTypeSelfRegisteringPlugin::FLAG_ANALYZER_TWICE, 1_622_727_282], ]; } diff --git a/tests/EndToEnd/PsalmEndToEndTest.php b/tests/EndToEnd/PsalmEndToEndTest.php index efc0d396c0b..4388a7a911f 100644 --- a/tests/EndToEnd/PsalmEndToEndTest.php +++ b/tests/EndToEnd/PsalmEndToEndTest.php @@ -152,7 +152,7 @@ public function testPsalmWithPHPVersionFromConfig(): void public function testPsalmDiff(): void { - if (PHP_VERSION_ID < 70400) { + if (PHP_VERSION_ID < 7_04_00) { $this->markTestSkipped('Only works on 7.4+'); } diff --git a/tests/FileDiffTest.php b/tests/FileDiffTest.php index f634abbc530..821e35b687f 100644 --- a/tests/FileDiffTest.php +++ b/tests/FileDiffTest.php @@ -37,8 +37,8 @@ public function testCode( $has_errors = false; - $a_stmts = StatementsProvider::parseStatements($a, 70400, $has_errors); - $b_stmts = StatementsProvider::parseStatements($b, 70400, $has_errors); + $a_stmts = StatementsProvider::parseStatements($a, 7_04_00, $has_errors); + $b_stmts = StatementsProvider::parseStatements($b, 7_04_00, $has_errors); $diff = FileStatementsDiffer::diff($a_stmts, $b_stmts, $a, $b); @@ -101,7 +101,7 @@ public function testPartialAstDiff( $has_errors = false; - $a_stmts = StatementsProvider::parseStatements($a, 70400, $has_errors); + $a_stmts = StatementsProvider::parseStatements($a, 7_04_00, $has_errors); $traverser = new PhpParser\NodeTraverser; $traverser->addVisitor(new CloningVisitor); @@ -111,8 +111,8 @@ public function testPartialAstDiff( $this->assertTreesEqual($a_stmts, $a_stmts_copy); - $b_stmts = StatementsProvider::parseStatements($b, 70400, $has_errors, null, $a, $a_stmts_copy, $file_changes); - $b_clean_stmts = StatementsProvider::parseStatements($b, 70400, $has_errors); + $b_stmts = StatementsProvider::parseStatements($b, 7_04_00, $has_errors, null, $a, $a_stmts_copy, $file_changes); + $b_clean_stmts = StatementsProvider::parseStatements($b, 7_04_00, $has_errors); $this->assertTreesEqual($b_clean_stmts, $b_stmts); @@ -1643,7 +1643,7 @@ public function bar() { [], ['c\a::foo', 'c\a::bar', 'c\a::zap', 'c\a::top', 'c\a::rot', 'c\a::bar'], [], - [[124, 405], [432, 711], [738, 1016], [1043, 1284]], + [[124, 405], [432, 711], [738, 1_016], [1_043, 1_284]], ], 'noUseChange' => [ ' Date: Tue, 4 Jan 2022 23:08:52 +0000 Subject: [PATCH 51/78] Break up two intersection methods At some later date it may be worth seeing whether these can be consolidated into a single intersection method --- .../Internal/Type/AssertionReconciler.php | 558 +++++++++--------- src/Psalm/Type.php | 185 +++--- 2 files changed, 381 insertions(+), 362 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index b7a1ef9a079..803b4fd1ac9 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -580,9 +580,6 @@ private static function refine( * precise version. For example: new is `array` old is `list` so the result is `list` * * @param array> $template_type_map - * - * @psalm-suppress ComplexMethod we'd probably want to extract specific handling blocks at the end and also allow - * early return once a specific case has been handled */ private static function filterTypeWithAnother( Codebase $codebase, @@ -594,347 +591,352 @@ private static function filterTypeWithAnother( ): Union { $matching_atomic_types = []; - $has_cloned_type = false; + $new_type = clone $new_type; foreach ($new_type->getAtomicTypes() as $new_type_part) { $has_local_match = false; - foreach ($existing_type->getAtomicTypes() as $key => $existing_type_part) { - // special workaround because PHP allows floats to contain ints, but we don’t want this - // behaviour here - if ($existing_type_part instanceof TFloat - && $new_type_part instanceof TInt - ) { - $any_scalar_type_match_found = true; - continue; - } - - $atomic_comparison_results = new TypeComparisonResult(); - - if ($existing_type_part instanceof TNamedObject) { - $existing_type_part->was_static = false; - } - - $atomic_contained_by = AtomicTypeComparator::isContainedBy( - $codebase, - $new_type_part, + foreach ($existing_type->getAtomicTypes() as $existing_type_part) { + $matching_atomic_type = self::filterAtomicWithAnother( $existing_type_part, - true, - false, - $atomic_comparison_results + $new_type_part, + $codebase, + $template_type_map, + $has_local_match, + $any_scalar_type_match_found ); - if ($atomic_contained_by) { - $has_local_match = true; - - if ($atomic_comparison_results->type_coerced - && get_class($new_type_part) === TNamedObject::class - && $existing_type_part instanceof TGenericObject - ) { - // this is a hack - it's not actually rigorous, as the params may be different - $matching_atomic_types[] = new TGenericObject( - $new_type_part->value, - $existing_type_part->type_params - ); - } elseif ($new_type_part instanceof TNamedObject - && $existing_type_part instanceof TTemplateParam - && $existing_type_part->as->hasObjectType() - ) { - $existing_type_part = clone $existing_type_part; - $existing_type_part->as = self::filterTypeWithAnother( - $codebase, - $existing_type_part->as, - new Union([$new_type_part]), - $template_type_map - ); - - $matching_atomic_types[] = $existing_type_part; - } else { - $matching_atomic_types[] = clone $new_type_part; - } - - continue; + if ($matching_atomic_type) { + $matching_atomic_types[] = $matching_atomic_type; } + } - if (AtomicTypeComparator::isContainedBy( - $codebase, - $existing_type_part, - $new_type_part, - false, - false, - null - )) { - $has_local_match = true; - $matching_atomic_types[] = $existing_type_part; + if (!$has_local_match) { + $has_match = false; + break; + } + } - continue; - } + if ($matching_atomic_types) { + $existing_type->bustCache(); + return new Union($matching_atomic_types); + } - if ($existing_type_part instanceof TNamedObject - && $new_type_part instanceof TNamedObject - && ($codebase->interfaceExists($existing_type_part->value) - || $codebase->interfaceExists($new_type_part->value)) - ) { - $matching_atomic_type = clone $new_type_part; - $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part; - $matching_atomic_types[] = $matching_atomic_type; - $has_local_match = true; + return $new_type; + } - continue; - } + /** + * @param array> $template_type_map + */ + private static function filterAtomicWithAnother( + Atomic $existing_type_part, + Atomic $new_type_part, + Codebase $codebase, + array $template_type_map, + bool &$has_local_match, + bool &$any_scalar_type_match_found, + ): ?Atomic { + if ($existing_type_part instanceof TFloat + && $new_type_part instanceof TInt + ) { + $any_scalar_type_match_found = true; + return $new_type_part; + } - if ($new_type_part instanceof TKeyedArray - && $existing_type_part instanceof TList - ) { - $new_type_key = $new_type_part->getGenericKeyType(); - $new_type_value = $new_type_part->getGenericValueType(); + $atomic_comparison_results = new TypeComparisonResult(); - if (!$new_type_key->hasString()) { - $has_param_match = false; + if ($existing_type_part instanceof TNamedObject) { + $existing_type_part->was_static = false; + } - $new_type_value = self::filterTypeWithAnother( - $codebase, - $existing_type_part->type_param, - $new_type_value, - $template_type_map, - $has_param_match, - $any_scalar_type_match_found - ); + $atomic_contained_by = AtomicTypeComparator::isContainedBy( + $codebase, + $new_type_part, + $existing_type_part, + true, + false, + $atomic_comparison_results + ); - $hybrid_type_part = new TKeyedArray($new_type_part->properties); - $hybrid_type_part->previous_key_type = Type::getInt(); - $hybrid_type_part->previous_value_type = $new_type_value; - $hybrid_type_part->is_list = true; + if ($atomic_contained_by) { + $has_local_match = true; - if (!$has_cloned_type) { - $new_type = clone $new_type; - $has_cloned_type = true; - } + if ($atomic_comparison_results->type_coerced + && get_class($new_type_part) === TNamedObject::class + && $existing_type_part instanceof TGenericObject + ) { + // this is a hack - it's not actually rigorous, as the params may be different + return new TGenericObject( + $new_type_part->value, + $existing_type_part->type_params + ); + } elseif ($new_type_part instanceof TNamedObject + && $existing_type_part instanceof TTemplateParam + && $existing_type_part->as->hasObjectType() + ) { + $existing_type_part = clone $existing_type_part; + $existing_type_part->as = self::filterTypeWithAnother( + $codebase, + $existing_type_part->as, + new Union([$new_type_part]), + $template_type_map + ); - $has_local_match = true; + return $existing_type_part; + } else { + return clone $new_type_part; + } + } - $new_type->removeType($key); - $new_type->addType($hybrid_type_part); + if (AtomicTypeComparator::isContainedBy( + $codebase, + $existing_type_part, + $new_type_part, + false, + false, + null + )) { + $has_local_match = true; - continue; - } - } + return $existing_type_part; + } - if ($new_type_part instanceof TTemplateParam - && $existing_type_part instanceof TTemplateParam - && $new_type_part->param_name !== $existing_type_part->param_name - && $new_type_part->as->hasObject() - && $existing_type_part->as->hasObject() - ) { - $matching_atomic_type = clone $new_type_part; + $matching_atomic_type = null; - $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part; - $matching_atomic_types[] = $matching_atomic_type; - $has_local_match = true; + if ($existing_type_part instanceof TNamedObject + && $new_type_part instanceof TNamedObject + && ($codebase->interfaceExists($existing_type_part->value) + || $codebase->interfaceExists($new_type_part->value)) + ) { + $matching_atomic_type = clone $new_type_part; + $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part; + $has_local_match = true; - continue; - } + return $matching_atomic_type; + } - //we filter both types of standard iterables - if (($new_type_part instanceof TGenericObject - || $new_type_part instanceof TArray - || $new_type_part instanceof TIterable) - && ($existing_type_part instanceof TGenericObject - || $existing_type_part instanceof TArray - || $existing_type_part instanceof TIterable) - && count($new_type_part->type_params) === count($existing_type_part->type_params) - ) { - $has_any_param_match = false; + if ($new_type_part instanceof TKeyedArray + && $existing_type_part instanceof TList + ) { + $new_type_key = $new_type_part->getGenericKeyType(); + $new_type_value = $new_type_part->getGenericValueType(); - foreach ($new_type_part->type_params as $i => $new_param) { - $existing_param = $existing_type_part->type_params[$i]; + if (!$new_type_key->hasString()) { + $has_param_match = false; - $has_param_match = true; + $new_type_value = self::filterTypeWithAnother( + $codebase, + $existing_type_part->type_param, + $new_type_value, + $template_type_map, + $has_param_match, + $any_scalar_type_match_found + ); - $new_param_id = $new_param->getId(); + $hybrid_type_part = new TKeyedArray($new_type_part->properties); + $hybrid_type_part->previous_key_type = Type::getInt(); + $hybrid_type_part->previous_value_type = $new_type_value; + $hybrid_type_part->is_list = true; - $new_param = self::filterTypeWithAnother( - $codebase, - $existing_param, - $new_param, - $template_type_map, - $has_param_match, - $any_scalar_type_match_found - ); + $has_local_match = true; - if ($template_type_map) { - TemplateInferredTypeReplacer::replace( - $new_param, - new TemplateResult([], $template_type_map), - $codebase - ); - } + return $hybrid_type_part; + } + } - $existing_type->bustCache(); + if ($new_type_part instanceof TTemplateParam + && $existing_type_part instanceof TTemplateParam + && $new_type_part->param_name !== $existing_type_part->param_name + && $new_type_part->as->hasObject() + && $existing_type_part->as->hasObject() + ) { + $matching_atomic_type = clone $new_type_part; - if ($has_param_match - && $existing_type_part->type_params[$i]->getId() !== $new_param_id - ) { - /** @psalm-suppress PropertyTypeCoercion */ - $existing_type_part->type_params[$i] = $new_param; + $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part; + $has_local_match = true; - if (!$has_local_match) { - $has_any_param_match = true; - } - } - } + return $matching_atomic_type; + } - if ($has_any_param_match) { - $has_local_match = true; - $matching_atomic_types[] = $existing_type_part; - $atomic_comparison_results->type_coerced = true; - } - } + //we filter both types of standard iterables + if (($new_type_part instanceof TGenericObject + || $new_type_part instanceof TArray + || $new_type_part instanceof TIterable) + && ($existing_type_part instanceof TGenericObject + || $existing_type_part instanceof TArray + || $existing_type_part instanceof TIterable) + && count($new_type_part->type_params) === count($existing_type_part->type_params) + ) { + $has_any_param_match = false; - //we filter the second part of a list with the second part of standard iterables - if (($new_type_part instanceof TArray - || $new_type_part instanceof TIterable) - && $existing_type_part instanceof TList - ) { - $has_any_param_match = false; + foreach ($new_type_part->type_params as $i => $new_param) { + $existing_param = $existing_type_part->type_params[$i]; - $new_param = $new_type_part->type_params[1]; - $existing_param = $existing_type_part->type_param; + $has_param_match = true; - $has_param_match = true; + $new_param_id = $new_param->getId(); - $new_param = self::filterTypeWithAnother( - $codebase, - $existing_param, + $new_param = self::filterTypeWithAnother( + $codebase, + $existing_param, + $new_param, + $template_type_map, + $has_param_match, + $any_scalar_type_match_found + ); + + if ($template_type_map) { + TemplateInferredTypeReplacer::replace( $new_param, - $template_type_map, - $has_param_match, - $any_scalar_type_match_found + new TemplateResult([], $template_type_map), + $codebase ); + } - if ($template_type_map) { - TemplateInferredTypeReplacer::replace( - $new_param, - new TemplateResult([], $template_type_map), - $codebase - ); + if ($has_param_match + && $existing_type_part->type_params[$i]->getId() !== $new_param_id + ) { + /** @psalm-suppress PropertyTypeCoercion */ + $existing_type_part->type_params[$i] = $new_param; + + if (!$has_local_match) { + $has_any_param_match = true; } + } + } - $existing_type->bustCache(); + if ($has_any_param_match) { + $has_local_match = true; + $matching_atomic_type = $existing_type_part; + $atomic_comparison_results->type_coerced = true; + } + } - if ($has_param_match - && $existing_type_part->type_param->getId() !== $new_param->getId() - ) { - $existing_type_part->type_param = $new_param; + //we filter the second part of a list with the second part of standard iterables + if (($new_type_part instanceof TArray + || $new_type_part instanceof TIterable) + && $existing_type_part instanceof TList + ) { + $has_any_param_match = false; - if (!$has_local_match) { - $has_any_param_match = true; - } - } + $new_param = $new_type_part->type_params[1]; + $existing_param = $existing_type_part->type_param; - if ($has_any_param_match) { - $has_local_match = true; - $matching_atomic_types[] = $existing_type_part; - $atomic_comparison_results->type_coerced = true; - } - } + $has_param_match = true; - //we filter each property of a Keyed Array with the second part of standard iterables - if (($new_type_part instanceof TArray - || $new_type_part instanceof TIterable) - && $existing_type_part instanceof TKeyedArray - ) { - $has_any_param_match = false; + $new_param = self::filterTypeWithAnother( + $codebase, + $existing_param, + $new_param, + $template_type_map, + $has_param_match, + $any_scalar_type_match_found + ); - $new_param = $new_type_part->type_params[1]; - foreach ($existing_type_part->properties as $property_key => $existing_param) { - $has_param_match = true; + if ($template_type_map) { + TemplateInferredTypeReplacer::replace( + $new_param, + new TemplateResult([], $template_type_map), + $codebase + ); + } - $new_param = self::filterTypeWithAnother( - $codebase, - $existing_param, - $new_param, - $template_type_map, - $has_param_match, - $any_scalar_type_match_found - ); + if ($has_param_match + && $existing_type_part->type_param->getId() !== $new_param->getId() + ) { + $existing_type_part->type_param = $new_param; - if ($template_type_map) { - TemplateInferredTypeReplacer::replace( - $new_param, - new TemplateResult([], $template_type_map), - $codebase - ); - } + if (!$has_local_match) { + $has_any_param_match = true; + } + } - if ($has_param_match - && $existing_type_part->properties[$property_key]->getId() !== $new_param->getId() - ) { - $existing_type_part->properties[$property_key] = $new_param; + if ($has_any_param_match) { + $has_local_match = true; + $matching_atomic_type = $existing_type_part; + $atomic_comparison_results->type_coerced = true; + } + } - if (!$has_local_match) { - $has_any_param_match = true; - } - } - } + //we filter each property of a Keyed Array with the second part of standard iterables + if (($new_type_part instanceof TArray + || $new_type_part instanceof TIterable) + && $existing_type_part instanceof TKeyedArray + ) { + $has_any_param_match = false; - $existing_type->bustCache(); + $new_param = $new_type_part->type_params[1]; + foreach ($existing_type_part->properties as $property_key => $existing_param) { + $has_param_match = true; - if ($has_any_param_match) { - $has_local_match = true; - $matching_atomic_types[] = $existing_type_part; - $atomic_comparison_results->type_coerced = true; - } - } + $new_param = self::filterTypeWithAnother( + $codebase, + $existing_param, + $new_param, + $template_type_map, + $has_param_match, + $any_scalar_type_match_found + ); - //These partial match wouldn't have been handled by AtomicTypeComparator - $new_range = null; - if ($new_type_part instanceof TIntRange && $existing_type_part instanceof TPositiveInt) { - $new_range = TIntRange::intersectIntRanges( - TIntRange::convertToIntRange($existing_type_part), - $new_type_part - ); - } elseif ($existing_type_part instanceof TIntRange - && $new_type_part instanceof TPositiveInt - ) { - $new_range = TIntRange::intersectIntRanges( - $existing_type_part, - TIntRange::convertToIntRange($new_type_part) - ); - } elseif ($new_type_part instanceof TIntRange - && $existing_type_part instanceof TIntRange - ) { - $new_range = TIntRange::intersectIntRanges( - $existing_type_part, - $new_type_part + if ($template_type_map) { + TemplateInferredTypeReplacer::replace( + $new_param, + new TemplateResult([], $template_type_map), + $codebase ); } - if ($new_range !== null) { - $has_local_match = true; - $matching_atomic_types[] = $new_range; - } - - if ($atomic_comparison_results->type_coerced) { - continue; - } + if ($has_param_match + && $existing_type_part->properties[$property_key]->getId() !== $new_param->getId() + ) { + $existing_type_part->properties[$property_key] = $new_param; - if ($atomic_comparison_results->scalar_type_match_found) { - $any_scalar_type_match_found = true; + if (!$has_local_match) { + $has_any_param_match = true; + } } } - if (!$has_local_match) { - $has_match = false; - break; + if ($has_any_param_match) { + $has_local_match = true; + $matching_atomic_type = $existing_type_part; + $atomic_comparison_results->type_coerced = true; } } - if ($matching_atomic_types) { - return new Union($matching_atomic_types); + //These partial match wouldn't have been handled by AtomicTypeComparator + $new_range = null; + if ($new_type_part instanceof TIntRange && $existing_type_part instanceof TPositiveInt) { + $new_range = TIntRange::intersectIntRanges( + TIntRange::convertToIntRange($existing_type_part), + $new_type_part + ); + } elseif ($existing_type_part instanceof TIntRange + && $new_type_part instanceof TPositiveInt + ) { + $new_range = TIntRange::intersectIntRanges( + $existing_type_part, + TIntRange::convertToIntRange($new_type_part) + ); + } elseif ($new_type_part instanceof TIntRange + && $existing_type_part instanceof TIntRange + ) { + $new_range = TIntRange::intersectIntRanges( + $existing_type_part, + $new_type_part + ); } - return $new_type; + if ($new_range !== null) { + $has_local_match = true; + $matching_atomic_type = $new_range; + } + + if (!$atomic_comparison_results->type_coerced && $atomic_comparison_results->scalar_type_match_found) { + $any_scalar_type_match_found = true; + } + + return $matching_atomic_type; } /** diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index 19591a88e28..a703aa7a6f2 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -585,91 +585,13 @@ public static function intersectUnionTypes( $combined_type = null; foreach ($type_1->getAtomicTypes() as $type_1_atomic) { foreach ($type_2->getAtomicTypes() as $type_2_atomic) { - $intersection_atomic = null; - $wider_type = null; - if ($type_1_atomic instanceof TNamedObject - && $type_2_atomic instanceof TNamedObject - ) { - if (($type_1_atomic->value === $type_2_atomic->value - && get_class($type_1_atomic) === TNamedObject::class - && get_class($type_2_atomic) !== TNamedObject::class) - ) { - $intersection_atomic = clone $type_2_atomic; - $wider_type = $type_1_atomic; - $intersection_performed = true; - } elseif (($type_1_atomic->value === $type_2_atomic->value - && get_class($type_2_atomic) === TNamedObject::class - && get_class($type_1_atomic) !== TNamedObject::class) - ) { - $intersection_atomic = clone $type_1_atomic; - $wider_type = $type_2_atomic; - $intersection_performed = true; - } - } - - if (null === $intersection_atomic) { - if (AtomicTypeComparator::isContainedBy( - $codebase, - $type_2_atomic, - $type_1_atomic - )) { - $intersection_atomic = clone $type_2_atomic; - $wider_type = $type_1_atomic; - $intersection_performed = true; - } elseif (AtomicTypeComparator::isContainedBy( - $codebase, - $type_1_atomic, - $type_2_atomic - )) { - $intersection_atomic = clone $type_1_atomic; - $wider_type = $type_2_atomic; - $intersection_performed = true; - } - } - - if (static::mayHaveIntersection($type_1_atomic) - && static::mayHaveIntersection($type_2_atomic) - ) { - if ($intersection_atomic === null && $wider_type === null) { - $intersection_atomic = clone $type_1_atomic; - $wider_type = $type_2_atomic; - } - if ($intersection_atomic === null || $wider_type === null) { - throw new LogicException( - '$intersection_atomic and $wider_type should be both set or null.' - .' Check the preceding code for errors.' - .' Did you forget to assign one of the variables?' - ); - } - if (!static::mayHaveIntersection($intersection_atomic) - || !static::mayHaveIntersection($wider_type) - ) { - throw new LogicException( - '$intersection_atomic and $wider_type should be both support intersection.' - .' Check the preceding code for errors.' - ); - } - if (!$intersection_atomic->extra_types) { - $intersection_atomic->extra_types = []; - } - - $intersection_performed = true; - - $wider_type_clone = clone $wider_type; + $intersection_atomic = self::intersectAtomicTypes( + $type_1_atomic, + $type_2_atomic, + $codebase, + $intersection_performed + ); - $wider_type_clone->extra_types = []; - - $intersection_atomic->extra_types[$wider_type_clone->getKey()] = $wider_type_clone; - - $wider_type_intersection_types = $wider_type->getIntersectionTypes(); - - if ($wider_type_intersection_types !== null) { - foreach ($wider_type_intersection_types as $wider_type_intersection_type) { - $intersection_atomic->extra_types[$wider_type_intersection_type->getKey()] - = clone $wider_type_intersection_type; - } - } - } if (null !== $intersection_atomic) { if (null === $combined_type) { $combined_type = new Union([$intersection_atomic]); @@ -736,6 +658,101 @@ public static function intersectUnionTypes( return $combined_type; } + private static function intersectAtomicTypes( + Atomic $type_1_atomic, + Atomic $type_2_atomic, + Codebase $codebase, + bool &$intersection_performed, + ): ?Atomic { + $intersection_atomic = null; + $wider_type = null; + if ($type_1_atomic instanceof TNamedObject + && $type_2_atomic instanceof TNamedObject + ) { + if (($type_1_atomic->value === $type_2_atomic->value + && get_class($type_1_atomic) === TNamedObject::class + && get_class($type_2_atomic) !== TNamedObject::class) + ) { + $intersection_atomic = clone $type_2_atomic; + $wider_type = $type_1_atomic; + $intersection_performed = true; + } elseif (($type_1_atomic->value === $type_2_atomic->value + && get_class($type_2_atomic) === TNamedObject::class + && get_class($type_1_atomic) !== TNamedObject::class) + ) { + $intersection_atomic = clone $type_1_atomic; + $wider_type = $type_2_atomic; + $intersection_performed = true; + } + } + + if (null === $intersection_atomic) { + if (AtomicTypeComparator::isContainedBy( + $codebase, + $type_2_atomic, + $type_1_atomic + )) { + $intersection_atomic = clone $type_2_atomic; + $wider_type = $type_1_atomic; + $intersection_performed = true; + } elseif (AtomicTypeComparator::isContainedBy( + $codebase, + $type_1_atomic, + $type_2_atomic + )) { + $intersection_atomic = clone $type_1_atomic; + $wider_type = $type_2_atomic; + $intersection_performed = true; + } + } + + if (static::mayHaveIntersection($type_1_atomic) + && static::mayHaveIntersection($type_2_atomic) + ) { + if ($intersection_atomic === null && $wider_type === null) { + $intersection_atomic = clone $type_1_atomic; + $wider_type = $type_2_atomic; + } + if ($intersection_atomic === null || $wider_type === null) { + throw new LogicException( + '$intersection_atomic and $wider_type should be both set or null.' + .' Check the preceding code for errors.' + .' Did you forget to assign one of the variables?' + ); + } + if (!static::mayHaveIntersection($intersection_atomic) + || !static::mayHaveIntersection($wider_type) + ) { + throw new LogicException( + '$intersection_atomic and $wider_type should be both support intersection.' + .' Check the preceding code for errors.' + ); + } + if (!$intersection_atomic->extra_types) { + $intersection_atomic->extra_types = []; + } + + $intersection_performed = true; + + $wider_type_clone = clone $wider_type; + + $wider_type_clone->extra_types = []; + + $intersection_atomic->extra_types[$wider_type_clone->getKey()] = $wider_type_clone; + + $wider_type_intersection_types = $wider_type->getIntersectionTypes(); + + if ($wider_type_intersection_types !== null) { + foreach ($wider_type_intersection_types as $wider_type_intersection_type) { + $intersection_atomic->extra_types[$wider_type_intersection_type->getKey()] + = clone $wider_type_intersection_type; + } + } + } + + return $intersection_atomic; + } + /** * @psalm-assert-if-true TIterable|TNamedObject|TTemplateParam|TObjectWithProperties $type */ From 0a78b320dacc5b681d5e7b599ecd9412f03512fb Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Tue, 4 Jan 2022 23:15:16 +0000 Subject: [PATCH 52/78] Remove commas for now --- src/Psalm/Internal/Type/AssertionReconciler.php | 2 +- src/Psalm/Type.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 803b4fd1ac9..ac3a7c3d25a 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -634,7 +634,7 @@ private static function filterAtomicWithAnother( Codebase $codebase, array $template_type_map, bool &$has_local_match, - bool &$any_scalar_type_match_found, + bool &$any_scalar_type_match_found ): ?Atomic { if ($existing_type_part instanceof TFloat && $new_type_part instanceof TInt diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index a703aa7a6f2..1d31caaac0a 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -662,7 +662,7 @@ private static function intersectAtomicTypes( Atomic $type_1_atomic, Atomic $type_2_atomic, Codebase $codebase, - bool &$intersection_performed, + bool &$intersection_performed ): ?Atomic { $intersection_atomic = null; $wider_type = null; From 5e22026863aed1f7dcc5e2e7732b92e0193e8b3d Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 5 Jan 2022 01:16:15 +0200 Subject: [PATCH 53/78] Fix version id for the callable object typehint --- src/Psalm/Type/Atomic/TCallableObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Type/Atomic/TCallableObject.php b/src/Psalm/Type/Atomic/TCallableObject.php index a10ea30526e..086fc4140c5 100644 --- a/src/Psalm/Type/Atomic/TCallableObject.php +++ b/src/Psalm/Type/Atomic/TCallableObject.php @@ -26,7 +26,7 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): ?string { - return $analysis_php_version_id >= 7_20_00 ? 'object' : null; + return $analysis_php_version_id >= 7_02_00 ? 'object' : null; } public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool From c81c5faa83a77a84022c78791542aedea81e565c Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 5 Jan 2022 01:49:27 +0200 Subject: [PATCH 54/78] Drop dead code based on PHP_VERSION_ID Psalm now requires PHP 7.4, so this test is never skipped. --- tests/EndToEnd/PsalmEndToEndTest.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/EndToEnd/PsalmEndToEndTest.php b/tests/EndToEnd/PsalmEndToEndTest.php index efc0d396c0b..59498a274df 100644 --- a/tests/EndToEnd/PsalmEndToEndTest.php +++ b/tests/EndToEnd/PsalmEndToEndTest.php @@ -24,8 +24,6 @@ use function tempnam; use function unlink; -use const PHP_VERSION_ID; - /** * Tests some of the most important use cases of the psalm and psalter commands, by launching a new * process as if invoked by a real user. @@ -152,10 +150,6 @@ public function testPsalmWithPHPVersionFromConfig(): void public function testPsalmDiff(): void { - if (PHP_VERSION_ID < 70400) { - $this->markTestSkipped('Only works on 7.4+'); - } - copy(__DIR__ . '/../fixtures/DummyProjectWithErrors/diff_composer.lock', self::$tmpDir . '/composer.lock'); $this->runPsalmInit(1); From 26de4faa5108e74ab231895185015c55d5f7a589 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 5 Jan 2022 00:38:40 +0000 Subject: [PATCH 55/78] Unify names with intersection creation --- .../Internal/Type/AssertionReconciler.php | 328 +++++++++++------- 1 file changed, 207 insertions(+), 121 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index ac3a7c3d25a..f0f84a5355b 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -506,7 +506,7 @@ private static function refine( ); } - $new_type = self::filterTypeWithAnother( + $intersection_type = self::filterTypeWithAnother( $codebase, $existing_var_type, $new_type, @@ -570,6 +570,10 @@ private static function refine( $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; } + + if ($intersection_type) { + $new_type = $intersection_type; + } } return $new_type; @@ -588,7 +592,7 @@ private static function filterTypeWithAnother( array $template_type_map, bool &$has_match = false, bool &$any_scalar_type_match_found = false - ): Union { + ): ?Union { $matching_atomic_types = []; $new_type = clone $new_type; @@ -622,38 +626,38 @@ private static function filterTypeWithAnother( return new Union($matching_atomic_types); } - return $new_type; + return null; } /** * @param array> $template_type_map */ private static function filterAtomicWithAnother( - Atomic $existing_type_part, - Atomic $new_type_part, + Atomic $type_1_atomic, + Atomic $type_2_atomic, Codebase $codebase, array $template_type_map, bool &$has_local_match, bool &$any_scalar_type_match_found ): ?Atomic { - if ($existing_type_part instanceof TFloat - && $new_type_part instanceof TInt + if ($type_1_atomic instanceof TFloat + && $type_2_atomic instanceof TInt ) { $any_scalar_type_match_found = true; - return $new_type_part; + return $type_2_atomic; } - $atomic_comparison_results = new TypeComparisonResult(); - - if ($existing_type_part instanceof TNamedObject) { - $existing_type_part->was_static = false; + if ($type_1_atomic instanceof TNamedObject) { + $type_1_atomic->was_static = false; } + $atomic_comparison_results = new TypeComparisonResult(); + $atomic_contained_by = AtomicTypeComparator::isContainedBy( $codebase, - $new_type_part, - $existing_type_part, - true, + $type_2_atomic, + $type_1_atomic, + true, // this probably should be false, but a few tests currently fail if it's not true false, $atomic_comparison_results ); @@ -661,81 +665,108 @@ private static function filterAtomicWithAnother( if ($atomic_contained_by) { $has_local_match = true; - if ($atomic_comparison_results->type_coerced - && get_class($new_type_part) === TNamedObject::class - && $existing_type_part instanceof TGenericObject - ) { - // this is a hack - it's not actually rigorous, as the params may be different - return new TGenericObject( - $new_type_part->value, - $existing_type_part->type_params - ); - } elseif ($new_type_part instanceof TNamedObject - && $existing_type_part instanceof TTemplateParam - && $existing_type_part->as->hasObjectType() - ) { - $existing_type_part = clone $existing_type_part; - $existing_type_part->as = self::filterTypeWithAnother( - $codebase, - $existing_type_part->as, - new Union([$new_type_part]), - $template_type_map - ); - - return $existing_type_part; - } else { - return clone $new_type_part; - } + return self::refineContainedAtomicWithAnother( + $type_1_atomic, + $type_2_atomic, + $codebase, + $template_type_map, + $atomic_comparison_results->type_coerced ?? false + ); } - if (AtomicTypeComparator::isContainedBy( + $atomic_comparison_results = new TypeComparisonResult(); + + $atomic_contained_by = AtomicTypeComparator::isContainedBy( $codebase, - $existing_type_part, - $new_type_part, + $type_1_atomic, + $type_2_atomic, false, false, - null - )) { + $atomic_comparison_results + ); + + if ($atomic_contained_by) { $has_local_match = true; - return $existing_type_part; + return self::refineContainedAtomicWithAnother( + $type_2_atomic, + $type_1_atomic, + $codebase, + $template_type_map, + $atomic_comparison_results->type_coerced ?? false + ); } $matching_atomic_type = null; - if ($existing_type_part instanceof TNamedObject - && $new_type_part instanceof TNamedObject - && ($codebase->interfaceExists($existing_type_part->value) - || $codebase->interfaceExists($new_type_part->value)) + if ($type_1_atomic instanceof TNamedObject + && $type_2_atomic instanceof TNamedObject + && ($codebase->interfaceExists($type_1_atomic->value) + || $codebase->interfaceExists($type_2_atomic->value)) ) { - $matching_atomic_type = clone $new_type_part; - $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part; + $matching_atomic_type = clone $type_2_atomic; + $matching_atomic_type->extra_types[$type_1_atomic->getKey()] = $type_1_atomic; $has_local_match = true; return $matching_atomic_type; } - if ($new_type_part instanceof TKeyedArray - && $existing_type_part instanceof TList + if ($type_2_atomic instanceof TKeyedArray + && $type_1_atomic instanceof TList + ) { + $type_2_key = $type_2_atomic->getGenericKeyType(); + $type_2_value = $type_2_atomic->getGenericValueType(); + + if (!$type_2_key->hasString()) { + $has_param_match = false; + + $type_2_value = self::filterTypeWithAnother( + $codebase, + $type_1_atomic->type_param, + $type_2_value, + $template_type_map, + $has_param_match, + $any_scalar_type_match_found + ); + + if ($type_2_value === null) { + return null; + } + + $hybrid_type_part = new TKeyedArray($type_2_atomic->properties); + $hybrid_type_part->previous_key_type = Type::getInt(); + $hybrid_type_part->previous_value_type = $type_2_value; + $hybrid_type_part->is_list = true; + + $has_local_match = true; + + return $hybrid_type_part; + } + } elseif ($type_1_atomic instanceof TKeyedArray + && $type_2_atomic instanceof TList ) { - $new_type_key = $new_type_part->getGenericKeyType(); - $new_type_value = $new_type_part->getGenericValueType(); + $type_1_key = $type_1_atomic->getGenericKeyType(); + $type_1_value = $type_1_atomic->getGenericValueType(); - if (!$new_type_key->hasString()) { + if (!$type_1_key->hasString()) { $has_param_match = false; - $new_type_value = self::filterTypeWithAnother( + $type_1_value = self::filterTypeWithAnother( $codebase, - $existing_type_part->type_param, - $new_type_value, + $type_2_atomic->type_param, + $type_1_value, $template_type_map, $has_param_match, $any_scalar_type_match_found ); - $hybrid_type_part = new TKeyedArray($new_type_part->properties); + if ($type_1_value === null) { + return null; + } + + $hybrid_type_part = new TKeyedArray($type_1_atomic->properties); $hybrid_type_part->previous_key_type = Type::getInt(); - $hybrid_type_part->previous_value_type = $new_type_value; + $hybrid_type_part->previous_value_type = $type_1_value; $hybrid_type_part->is_list = true; $has_local_match = true; @@ -744,60 +775,64 @@ private static function filterAtomicWithAnother( } } - if ($new_type_part instanceof TTemplateParam - && $existing_type_part instanceof TTemplateParam - && $new_type_part->param_name !== $existing_type_part->param_name - && $new_type_part->as->hasObject() - && $existing_type_part->as->hasObject() + if ($type_2_atomic instanceof TTemplateParam + && $type_1_atomic instanceof TTemplateParam + && $type_2_atomic->param_name !== $type_1_atomic->param_name + && $type_2_atomic->as->hasObject() + && $type_1_atomic->as->hasObject() ) { - $matching_atomic_type = clone $new_type_part; + $matching_atomic_type = clone $type_2_atomic; - $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part; + $matching_atomic_type->extra_types[$type_1_atomic->getKey()] = $type_1_atomic; $has_local_match = true; return $matching_atomic_type; } //we filter both types of standard iterables - if (($new_type_part instanceof TGenericObject - || $new_type_part instanceof TArray - || $new_type_part instanceof TIterable) - && ($existing_type_part instanceof TGenericObject - || $existing_type_part instanceof TArray - || $existing_type_part instanceof TIterable) - && count($new_type_part->type_params) === count($existing_type_part->type_params) + if (($type_2_atomic instanceof TGenericObject + || $type_2_atomic instanceof TArray + || $type_2_atomic instanceof TIterable) + && ($type_1_atomic instanceof TGenericObject + || $type_1_atomic instanceof TArray + || $type_1_atomic instanceof TIterable) + && count($type_2_atomic->type_params) === count($type_1_atomic->type_params) ) { $has_any_param_match = false; - foreach ($new_type_part->type_params as $i => $new_param) { - $existing_param = $existing_type_part->type_params[$i]; + foreach ($type_2_atomic->type_params as $i => $type_2_param) { + $type_1_param = $type_1_atomic->type_params[$i]; $has_param_match = true; - $new_param_id = $new_param->getId(); + $type_2_param_id = $type_2_param->getId(); - $new_param = self::filterTypeWithAnother( + $type_2_param = self::filterTypeWithAnother( $codebase, - $existing_param, - $new_param, + $type_1_param, + $type_2_param, $template_type_map, $has_param_match, $any_scalar_type_match_found ); + if ($type_2_param === null) { + return null; + } + if ($template_type_map) { TemplateInferredTypeReplacer::replace( - $new_param, + $type_2_param, new TemplateResult([], $template_type_map), $codebase ); } if ($has_param_match - && $existing_type_part->type_params[$i]->getId() !== $new_param_id + && $type_1_atomic->type_params[$i]->getId() !== $type_2_param_id ) { /** @psalm-suppress PropertyTypeCoercion */ - $existing_type_part->type_params[$i] = $new_param; + $type_1_atomic->type_params[$i] = $type_2_param; if (!$has_local_match) { $has_any_param_match = true; @@ -807,44 +842,48 @@ private static function filterAtomicWithAnother( if ($has_any_param_match) { $has_local_match = true; - $matching_atomic_type = $existing_type_part; + $matching_atomic_type = $type_1_atomic; $atomic_comparison_results->type_coerced = true; } } //we filter the second part of a list with the second part of standard iterables - if (($new_type_part instanceof TArray - || $new_type_part instanceof TIterable) - && $existing_type_part instanceof TList + if (($type_2_atomic instanceof TArray + || $type_2_atomic instanceof TIterable) + && $type_1_atomic instanceof TList ) { $has_any_param_match = false; - $new_param = $new_type_part->type_params[1]; - $existing_param = $existing_type_part->type_param; + $type_2_param = $type_2_atomic->type_params[1]; + $type_1_param = $type_1_atomic->type_param; $has_param_match = true; - $new_param = self::filterTypeWithAnother( + $type_2_param = self::filterTypeWithAnother( $codebase, - $existing_param, - $new_param, + $type_1_param, + $type_2_param, $template_type_map, $has_param_match, $any_scalar_type_match_found ); + if ($type_2_param === null) { + return null; + } + if ($template_type_map) { TemplateInferredTypeReplacer::replace( - $new_param, + $type_2_param, new TemplateResult([], $template_type_map), $codebase ); } if ($has_param_match - && $existing_type_part->type_param->getId() !== $new_param->getId() + && $type_1_atomic->type_param->getId() !== $type_2_param->getId() ) { - $existing_type_part->type_param = $new_param; + $type_1_atomic->type_param = $type_2_param; if (!$has_local_match) { $has_any_param_match = true; @@ -853,43 +892,47 @@ private static function filterAtomicWithAnother( if ($has_any_param_match) { $has_local_match = true; - $matching_atomic_type = $existing_type_part; + $matching_atomic_type = $type_1_atomic; $atomic_comparison_results->type_coerced = true; } } //we filter each property of a Keyed Array with the second part of standard iterables - if (($new_type_part instanceof TArray - || $new_type_part instanceof TIterable) - && $existing_type_part instanceof TKeyedArray + if (($type_2_atomic instanceof TArray + || $type_2_atomic instanceof TIterable) + && $type_1_atomic instanceof TKeyedArray ) { $has_any_param_match = false; - $new_param = $new_type_part->type_params[1]; - foreach ($existing_type_part->properties as $property_key => $existing_param) { + $type_2_param = $type_2_atomic->type_params[1]; + foreach ($type_1_atomic->properties as $property_key => $type_1_param) { $has_param_match = true; - $new_param = self::filterTypeWithAnother( + $type_2_param = self::filterTypeWithAnother( $codebase, - $existing_param, - $new_param, + $type_1_param, + $type_2_param, $template_type_map, $has_param_match, $any_scalar_type_match_found ); + if ($type_2_param === null) { + return null; + } + if ($template_type_map) { TemplateInferredTypeReplacer::replace( - $new_param, + $type_2_param, new TemplateResult([], $template_type_map), $codebase ); } if ($has_param_match - && $existing_type_part->properties[$property_key]->getId() !== $new_param->getId() + && $type_1_atomic->properties[$property_key]->getId() !== $type_2_param->getId() ) { - $existing_type_part->properties[$property_key] = $new_param; + $type_1_atomic->properties[$property_key] = $type_2_param; if (!$has_local_match) { $has_any_param_match = true; @@ -899,31 +942,31 @@ private static function filterAtomicWithAnother( if ($has_any_param_match) { $has_local_match = true; - $matching_atomic_type = $existing_type_part; + $matching_atomic_type = $type_1_atomic; $atomic_comparison_results->type_coerced = true; } } //These partial match wouldn't have been handled by AtomicTypeComparator $new_range = null; - if ($new_type_part instanceof TIntRange && $existing_type_part instanceof TPositiveInt) { + if ($type_2_atomic instanceof TIntRange && $type_1_atomic instanceof TPositiveInt) { $new_range = TIntRange::intersectIntRanges( - TIntRange::convertToIntRange($existing_type_part), - $new_type_part + TIntRange::convertToIntRange($type_1_atomic), + $type_2_atomic ); - } elseif ($existing_type_part instanceof TIntRange - && $new_type_part instanceof TPositiveInt + } elseif ($type_1_atomic instanceof TIntRange + && $type_2_atomic instanceof TPositiveInt ) { $new_range = TIntRange::intersectIntRanges( - $existing_type_part, - TIntRange::convertToIntRange($new_type_part) + $type_1_atomic, + TIntRange::convertToIntRange($type_2_atomic) ); - } elseif ($new_type_part instanceof TIntRange - && $existing_type_part instanceof TIntRange + } elseif ($type_2_atomic instanceof TIntRange + && $type_1_atomic instanceof TIntRange ) { $new_range = TIntRange::intersectIntRanges( - $existing_type_part, - $new_type_part + $type_1_atomic, + $type_2_atomic ); } @@ -939,6 +982,49 @@ private static function filterAtomicWithAnother( return $matching_atomic_type; } + /** + * @param array> $template_type_map + */ + private static function refineContainedAtomicWithAnother( + Atomic $type_1_atomic, + Atomic $type_2_atomic, + Codebase $codebase, + array $template_type_map, + bool $type_coerced + ): ?Atomic { + if ($type_coerced + && get_class($type_2_atomic) === TNamedObject::class + && $type_1_atomic instanceof TGenericObject + ) { + // this is a hack - it's not actually rigorous, as the params may be different + return new TGenericObject( + $type_2_atomic->value, + $type_1_atomic->type_params + ); + } elseif ($type_2_atomic instanceof TNamedObject + && $type_1_atomic instanceof TTemplateParam + && $type_1_atomic->as->hasObjectType() + ) { + $type_1_atomic = clone $type_1_atomic; + $type_1_as = self::filterTypeWithAnother( + $codebase, + $type_1_atomic->as, + new Union([$type_2_atomic]), + $template_type_map + ); + + if ($type_1_as === null) { + return null; + } + + $type_1_atomic->as = $type_1_as; + + return $type_1_atomic; + } else { + return clone $type_2_atomic; + } + } + /** * @param string[] $suppressed_issues */ From 31dd30f3d1db2072b69420673e5c133190a69cbe Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 5 Jan 2022 00:50:09 +0000 Subject: [PATCH 56/78] Reduce by-ref args --- .../Internal/Type/AssertionReconciler.php | 92 ++----------------- 1 file changed, 10 insertions(+), 82 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index f0f84a5355b..308e64d9447 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -471,8 +471,6 @@ private static function refine( return new Union($acceptable_atomic_types); } } elseif (!$new_type->hasMixed()) { - $has_match = true; - $any_scalar_type_match_found = false; if ($code_location @@ -511,12 +509,11 @@ private static function refine( $existing_var_type, $new_type, $template_type_map, - $has_match, $any_scalar_type_match_found ); if ($code_location - && !$has_match + && !$intersection_type && (!$is_loose_equality || !$any_scalar_type_match_found) ) { if ($assertion === 'null') { @@ -590,7 +587,6 @@ private static function filterTypeWithAnother( Union $existing_type, Union $new_type, array $template_type_map, - bool &$has_match = false, bool &$any_scalar_type_match_found = false ): ?Union { $matching_atomic_types = []; @@ -598,15 +594,12 @@ private static function filterTypeWithAnother( $new_type = clone $new_type; foreach ($new_type->getAtomicTypes() as $new_type_part) { - $has_local_match = false; - foreach ($existing_type->getAtomicTypes() as $existing_type_part) { $matching_atomic_type = self::filterAtomicWithAnother( $existing_type_part, $new_type_part, $codebase, $template_type_map, - $has_local_match, $any_scalar_type_match_found ); @@ -614,11 +607,6 @@ private static function filterTypeWithAnother( $matching_atomic_types[] = $matching_atomic_type; } } - - if (!$has_local_match) { - $has_match = false; - break; - } } if ($matching_atomic_types) { @@ -637,7 +625,6 @@ private static function filterAtomicWithAnother( Atomic $type_2_atomic, Codebase $codebase, array $template_type_map, - bool &$has_local_match, bool &$any_scalar_type_match_found ): ?Atomic { if ($type_1_atomic instanceof TFloat @@ -663,8 +650,6 @@ private static function filterAtomicWithAnother( ); if ($atomic_contained_by) { - $has_local_match = true; - return self::refineContainedAtomicWithAnother( $type_1_atomic, $type_2_atomic, @@ -686,8 +671,6 @@ private static function filterAtomicWithAnother( ); if ($atomic_contained_by) { - $has_local_match = true; - return self::refineContainedAtomicWithAnother( $type_2_atomic, $type_1_atomic, @@ -706,7 +689,6 @@ private static function filterAtomicWithAnother( ) { $matching_atomic_type = clone $type_2_atomic; $matching_atomic_type->extra_types[$type_1_atomic->getKey()] = $type_1_atomic; - $has_local_match = true; return $matching_atomic_type; } @@ -718,14 +700,11 @@ private static function filterAtomicWithAnother( $type_2_value = $type_2_atomic->getGenericValueType(); if (!$type_2_key->hasString()) { - $has_param_match = false; - $type_2_value = self::filterTypeWithAnother( $codebase, $type_1_atomic->type_param, $type_2_value, $template_type_map, - $has_param_match, $any_scalar_type_match_found ); @@ -738,8 +717,6 @@ private static function filterAtomicWithAnother( $hybrid_type_part->previous_value_type = $type_2_value; $hybrid_type_part->is_list = true; - $has_local_match = true; - return $hybrid_type_part; } } elseif ($type_1_atomic instanceof TKeyedArray @@ -749,14 +726,11 @@ private static function filterAtomicWithAnother( $type_1_value = $type_1_atomic->getGenericValueType(); if (!$type_1_key->hasString()) { - $has_param_match = false; - $type_1_value = self::filterTypeWithAnother( $codebase, $type_2_atomic->type_param, $type_1_value, $template_type_map, - $has_param_match, $any_scalar_type_match_found ); @@ -769,8 +743,6 @@ private static function filterAtomicWithAnother( $hybrid_type_part->previous_value_type = $type_1_value; $hybrid_type_part->is_list = true; - $has_local_match = true; - return $hybrid_type_part; } } @@ -784,7 +756,6 @@ private static function filterAtomicWithAnother( $matching_atomic_type = clone $type_2_atomic; $matching_atomic_type->extra_types[$type_1_atomic->getKey()] = $type_1_atomic; - $has_local_match = true; return $matching_atomic_type; } @@ -798,13 +769,9 @@ private static function filterAtomicWithAnother( || $type_1_atomic instanceof TIterable) && count($type_2_atomic->type_params) === count($type_1_atomic->type_params) ) { - $has_any_param_match = false; - foreach ($type_2_atomic->type_params as $i => $type_2_param) { $type_1_param = $type_1_atomic->type_params[$i]; - $has_param_match = true; - $type_2_param_id = $type_2_param->getId(); $type_2_param = self::filterTypeWithAnother( @@ -812,7 +779,6 @@ private static function filterAtomicWithAnother( $type_1_param, $type_2_param, $template_type_map, - $has_param_match, $any_scalar_type_match_found ); @@ -828,23 +794,14 @@ private static function filterAtomicWithAnother( ); } - if ($has_param_match - && $type_1_atomic->type_params[$i]->getId() !== $type_2_param_id - ) { + if ($type_1_atomic->type_params[$i]->getId() !== $type_2_param_id) { /** @psalm-suppress PropertyTypeCoercion */ $type_1_atomic->type_params[$i] = $type_2_param; - - if (!$has_local_match) { - $has_any_param_match = true; - } } } - if ($has_any_param_match) { - $has_local_match = true; - $matching_atomic_type = $type_1_atomic; - $atomic_comparison_results->type_coerced = true; - } + $matching_atomic_type = $type_1_atomic; + $atomic_comparison_results->type_coerced = true; } //we filter the second part of a list with the second part of standard iterables @@ -852,19 +809,14 @@ private static function filterAtomicWithAnother( || $type_2_atomic instanceof TIterable) && $type_1_atomic instanceof TList ) { - $has_any_param_match = false; - $type_2_param = $type_2_atomic->type_params[1]; $type_1_param = $type_1_atomic->type_param; - $has_param_match = true; - $type_2_param = self::filterTypeWithAnother( $codebase, $type_1_param, $type_2_param, $template_type_map, - $has_param_match, $any_scalar_type_match_found ); @@ -880,21 +832,12 @@ private static function filterAtomicWithAnother( ); } - if ($has_param_match - && $type_1_atomic->type_param->getId() !== $type_2_param->getId() - ) { + if ($type_1_atomic->type_param->getId() !== $type_2_param->getId()) { $type_1_atomic->type_param = $type_2_param; - - if (!$has_local_match) { - $has_any_param_match = true; - } } - if ($has_any_param_match) { - $has_local_match = true; - $matching_atomic_type = $type_1_atomic; - $atomic_comparison_results->type_coerced = true; - } + $matching_atomic_type = $type_1_atomic; + $atomic_comparison_results->type_coerced = true; } //we filter each property of a Keyed Array with the second part of standard iterables @@ -902,18 +845,13 @@ private static function filterAtomicWithAnother( || $type_2_atomic instanceof TIterable) && $type_1_atomic instanceof TKeyedArray ) { - $has_any_param_match = false; - $type_2_param = $type_2_atomic->type_params[1]; foreach ($type_1_atomic->properties as $property_key => $type_1_param) { - $has_param_match = true; - $type_2_param = self::filterTypeWithAnother( $codebase, $type_1_param, $type_2_param, $template_type_map, - $has_param_match, $any_scalar_type_match_found ); @@ -929,22 +867,13 @@ private static function filterAtomicWithAnother( ); } - if ($has_param_match - && $type_1_atomic->properties[$property_key]->getId() !== $type_2_param->getId() - ) { + if ($type_1_atomic->properties[$property_key]->getId() !== $type_2_param->getId()) { $type_1_atomic->properties[$property_key] = $type_2_param; - - if (!$has_local_match) { - $has_any_param_match = true; - } } } - if ($has_any_param_match) { - $has_local_match = true; - $matching_atomic_type = $type_1_atomic; - $atomic_comparison_results->type_coerced = true; - } + $matching_atomic_type = $type_1_atomic; + $atomic_comparison_results->type_coerced = true; } //These partial match wouldn't have been handled by AtomicTypeComparator @@ -971,7 +900,6 @@ private static function filterAtomicWithAnother( } if ($new_range !== null) { - $has_local_match = true; $matching_atomic_type = $new_range; } From 7595a3c579e09e29a321fea88602661501c792d5 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 5 Jan 2022 01:10:57 +0000 Subject: [PATCH 57/78] Make treatment of interfaces and classes more symmetrical --- src/Psalm/Internal/Type/AssertionReconciler.php | 4 ++-- tests/TypeReconciliation/ReconcilerTest.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 308e64d9447..9a47a2fb5e9 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -644,7 +644,7 @@ private static function filterAtomicWithAnother( $codebase, $type_2_atomic, $type_1_atomic, - true, // this probably should be false, but a few tests currently fail if it's not true + !($type_1_atomic instanceof TNamedObject && $type_2_atomic instanceof TNamedObject), false, $atomic_comparison_results ); @@ -665,7 +665,7 @@ private static function filterAtomicWithAnother( $codebase, $type_1_atomic, $type_2_atomic, - false, + !($type_1_atomic instanceof TNamedObject && $type_2_atomic instanceof TNamedObject), false, $atomic_comparison_results ); diff --git a/tests/TypeReconciliation/ReconcilerTest.php b/tests/TypeReconciliation/ReconcilerTest.php index f592a750f83..e5edec8d7f3 100644 --- a/tests/TypeReconciliation/ReconcilerTest.php +++ b/tests/TypeReconciliation/ReconcilerTest.php @@ -146,6 +146,7 @@ public function providerTestReconcilation(): array 'iterableAndNotObject' => ['array', '!object', 'iterable'], 'boolNotEmptyIsTrue' => ['true', '!empty', 'bool'], 'interfaceAssertionOnClassInterfaceUnion' => ['SomeInterface|SomeInterface&SomeClass', 'SomeInterface', 'SomeClass|SomeInterface'], + 'classAssertionOnClassInterfaceUnion' => ['SomeClass|SomeClass&SomeInterface', 'SomeClass', 'SomeClass|SomeInterface'], 'stringToNumericStringWithInt' => ['numeric-string', '~int', 'string'], 'stringToNumericStringWithFloat' => ['numeric-string', '~float', 'string'], 'filterKeyedArrayWithIterable' => ['array{some: string}', 'iterable', 'array{some: mixed}'], From f5d6d2380f0995ab02d236a7cb710d480c30fd3a Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 5 Jan 2022 01:54:00 +0000 Subject: [PATCH 58/78] Remove now-unnecessary code Originally added in 8bc17e47bec0ae89429cd35442f9d2ca61053201 --- .../Internal/Type/AssertionReconciler.php | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 9a47a2fb5e9..75096d05a2e 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -508,7 +508,6 @@ private static function refine( $codebase, $existing_var_type, $new_type, - $template_type_map, $any_scalar_type_match_found ); @@ -586,7 +585,6 @@ private static function filterTypeWithAnother( Codebase $codebase, Union $existing_type, Union $new_type, - array $template_type_map, bool &$any_scalar_type_match_found = false ): ?Union { $matching_atomic_types = []; @@ -599,7 +597,6 @@ private static function filterTypeWithAnother( $existing_type_part, $new_type_part, $codebase, - $template_type_map, $any_scalar_type_match_found ); @@ -617,14 +614,10 @@ private static function filterTypeWithAnother( return null; } - /** - * @param array> $template_type_map - */ private static function filterAtomicWithAnother( Atomic $type_1_atomic, Atomic $type_2_atomic, Codebase $codebase, - array $template_type_map, bool &$any_scalar_type_match_found ): ?Atomic { if ($type_1_atomic instanceof TFloat @@ -654,7 +647,6 @@ private static function filterAtomicWithAnother( $type_1_atomic, $type_2_atomic, $codebase, - $template_type_map, $atomic_comparison_results->type_coerced ?? false ); } @@ -675,7 +667,6 @@ private static function filterAtomicWithAnother( $type_2_atomic, $type_1_atomic, $codebase, - $template_type_map, $atomic_comparison_results->type_coerced ?? false ); } @@ -704,7 +695,6 @@ private static function filterAtomicWithAnother( $codebase, $type_1_atomic->type_param, $type_2_value, - $template_type_map, $any_scalar_type_match_found ); @@ -730,7 +720,6 @@ private static function filterAtomicWithAnother( $codebase, $type_2_atomic->type_param, $type_1_value, - $template_type_map, $any_scalar_type_match_found ); @@ -778,7 +767,6 @@ private static function filterAtomicWithAnother( $codebase, $type_1_param, $type_2_param, - $template_type_map, $any_scalar_type_match_found ); @@ -786,14 +774,6 @@ private static function filterAtomicWithAnother( return null; } - if ($template_type_map) { - TemplateInferredTypeReplacer::replace( - $type_2_param, - new TemplateResult([], $template_type_map), - $codebase - ); - } - if ($type_1_atomic->type_params[$i]->getId() !== $type_2_param_id) { /** @psalm-suppress PropertyTypeCoercion */ $type_1_atomic->type_params[$i] = $type_2_param; @@ -816,7 +796,6 @@ private static function filterAtomicWithAnother( $codebase, $type_1_param, $type_2_param, - $template_type_map, $any_scalar_type_match_found ); @@ -824,14 +803,6 @@ private static function filterAtomicWithAnother( return null; } - if ($template_type_map) { - TemplateInferredTypeReplacer::replace( - $type_2_param, - new TemplateResult([], $template_type_map), - $codebase - ); - } - if ($type_1_atomic->type_param->getId() !== $type_2_param->getId()) { $type_1_atomic->type_param = $type_2_param; } @@ -851,7 +822,6 @@ private static function filterAtomicWithAnother( $codebase, $type_1_param, $type_2_param, - $template_type_map, $any_scalar_type_match_found ); @@ -859,14 +829,6 @@ private static function filterAtomicWithAnother( return null; } - if ($template_type_map) { - TemplateInferredTypeReplacer::replace( - $type_2_param, - new TemplateResult([], $template_type_map), - $codebase - ); - } - if ($type_1_atomic->properties[$property_key]->getId() !== $type_2_param->getId()) { $type_1_atomic->properties[$property_key] = $type_2_param; } @@ -910,14 +872,10 @@ private static function filterAtomicWithAnother( return $matching_atomic_type; } - /** - * @param array> $template_type_map - */ private static function refineContainedAtomicWithAnother( Atomic $type_1_atomic, Atomic $type_2_atomic, Codebase $codebase, - array $template_type_map, bool $type_coerced ): ?Atomic { if ($type_coerced @@ -937,8 +895,7 @@ private static function refineContainedAtomicWithAnother( $type_1_as = self::filterTypeWithAnother( $codebase, $type_1_atomic->as, - new Union([$type_2_atomic]), - $template_type_map + new Union([$type_2_atomic]) ); if ($type_1_as === null) { From d30d527aeb3396e61f8b314d2319ef7a4bfa6404 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 5 Jan 2022 04:11:52 +0200 Subject: [PATCH 59/78] Ensure separator is used for long numbers I would like to set minDigitsBeforeDecimalPoint to 4, but it causes false positives with octal numbers (slevomat/coding-standard#1324). --- phpcs.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/phpcs.xml b/phpcs.xml index 627943d181d..7bd45e97fdf 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -190,4 +190,11 @@ + + + + + + + From dd094500921e5241722d6c2628e66e9a22e9a74c Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 5 Jan 2022 13:09:37 +0200 Subject: [PATCH 60/78] Applied NullCoalescingOperatorRector --- .../Internal/Analyzer/Statements/Block/ForeachAnalyzer.php | 4 ++-- .../Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php | 2 +- .../ReturnTypeProvider/ArrayMergeReturnTypeProvider.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index 852abdc8f62..2c7fe74f9c6 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -279,7 +279,7 @@ public static function analyze( } if ($stmt->keyVar instanceof PhpParser\Node\Expr\Variable && is_string($stmt->keyVar->name)) { - $key_type = $key_type ?? Type::getMixed(); + $key_type ??= Type::getMixed(); AssignmentAnalyzer::analyze( $statements_analyzer, @@ -292,7 +292,7 @@ public static function analyze( ); } - $value_type = $value_type ?? Type::getMixed(); + $value_type ??= Type::getMixed(); if ($stmt->byRef) { $value_type->by_ref = true; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php index f412b89d7b7..719b9225ae7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php @@ -419,7 +419,7 @@ function (Assertion $assertion) use ($generic_params, $codebase): Assertion { } } - $return_type_candidate = $return_type_candidate ?? Type::getMixed(); + $return_type_candidate ??= Type::getMixed(); StaticCallAnalyzer::taintReturnType( $statements_analyzer, diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index c58b44d512d..5ffc0a1e626 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -246,7 +246,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev ]); } - $inner_key_type = $inner_key_type ?? Type::getArrayKey(); + $inner_key_type ??= Type::getArrayKey(); if ($any_nonempty) { return new Union([ From dfb14cbe872169ab3ad24bc9e6e1e29da96a2640 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 5 Jan 2022 13:21:30 +0200 Subject: [PATCH 61/78] Enforce null-coalesce operators --- phpcs.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/phpcs.xml b/phpcs.xml index 7bd45e97fdf..f1c27e858ca 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -190,6 +190,10 @@ + @@ -197,4 +201,19 @@ + + + + + + + + + From 94395f18a4e2f97c1df5b9063866eaf280bdcc35 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 5 Jan 2022 19:32:43 +0200 Subject: [PATCH 62/78] Applied JsonThrowOnErrorRector --- src/Psalm/Config.php | 10 +++++++++- src/Psalm/Config/Creator.php | 17 +++++++++++++++-- src/Psalm/Context.php | 4 +++- .../Statements/Expression/AssertionFinder.php | 4 +++- src/Psalm/Internal/Clause.php | 4 +++- src/Psalm/Internal/Cli/Psalm.php | 6 +++++- src/Psalm/Internal/CliUtils.php | 13 ++++++++++++- src/Psalm/Internal/Codebase/TaintFlowGraph.php | 6 ++++-- .../ExecutionEnvironment/BuildInfoCollector.php | 4 +++- .../PluginManager/PluginListFactory.php | 4 +++- .../Internal/Provider/ParserCacheProvider.php | 13 +++++++++++-- src/Psalm/Plugin/Shepherd.php | 3 ++- src/Psalm/Type/Reconciler.php | 6 ++++-- tests/ComposerLockTest.php | 4 +++- tests/ReportOutputTest.php | 10 ++++++---- 15 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 4e2978649cd..6f679023e83 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -9,6 +9,7 @@ use DOMDocument; use DomElement; use InvalidArgumentException; +use JsonException; use LogicException; use OutOfBoundsException; use Psalm\CodeLocation\Raw; @@ -102,6 +103,7 @@ use const DIRECTORY_SEPARATOR; use const E_USER_ERROR; use const GLOB_NOSORT; +use const JSON_THROW_ON_ERROR; use const LIBXML_ERR_ERROR; use const LIBXML_ERR_FATAL; use const LIBXML_NONET; @@ -2256,7 +2258,13 @@ public function getPHPVersionFromComposerJson(): ?string $composer_json_path = Composer::getJsonFilePath($this->base_dir); if (file_exists($composer_json_path)) { - if (!$composer_json = json_decode(file_get_contents($composer_json_path), true)) { + try { + $composer_json = json_decode(file_get_contents($composer_json_path), true, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + $composer_json = null; + } + + if (!$composer_json) { throw new UnexpectedValueException('Invalid composer.json at ' . $composer_json_path); } $php_version = $composer_json['require']['php'] ?? null; diff --git a/src/Psalm/Config/Creator.php b/src/Psalm/Config/Creator.php index 36760726e1c..bf837b666e4 100644 --- a/src/Psalm/Config/Creator.php +++ b/src/Psalm/Config/Creator.php @@ -2,6 +2,7 @@ namespace Psalm\Config; +use JsonException; use Psalm\Config; use Psalm\Exception\ConfigCreationException; use Psalm\Internal\Analyzer\IssueData; @@ -32,6 +33,7 @@ use const DIRECTORY_SEPARATOR; use const GLOB_NOSORT; +use const JSON_THROW_ON_ERROR; class Creator { @@ -184,8 +186,19 @@ public static function getPaths(string $current_dir, ?string $suggested_dir): ar 'Problem during config autodiscovery - could not find composer.json during initialization.' ); } - - if (!$composer_json = json_decode(file_get_contents($composer_json_location), true)) { + try { + $composer_json = json_decode( + file_get_contents($composer_json_location), + true, + 512, + JSON_THROW_ON_ERROR + ); + } catch (JsonException $e) { + throw new ConfigCreationException( + 'Invalid composer.json at ' . $composer_json_location . ': ' . $e->getMessage() + ); + } + if (!$composer_json) { throw new ConfigCreationException('Invalid composer.json at ' . $composer_json_location); } diff --git a/src/Psalm/Context.php b/src/Psalm/Context.php index 3b9470d6f64..78b116ba034 100644 --- a/src/Psalm/Context.php +++ b/src/Psalm/Context.php @@ -27,6 +27,8 @@ use function strpos; use function strtolower; +use const JSON_THROW_ON_ERROR; + class Context { /** @@ -770,7 +772,7 @@ public function getScopeSummary(): string $summary[$k] = $v->getId(); } - return json_encode($summary); + return json_encode($summary, JSON_THROW_ON_ERROR); } public function defineGlobals(): void diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index c5854130a2c..87706f98c96 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -71,6 +71,8 @@ use function strtolower; use function substr; +use const JSON_THROW_ON_ERROR; + /** * @internal * This class transform conditions in code into "assertions" that will be reconciled with the type already known of a @@ -149,7 +151,7 @@ public static function scrapeAssertions( if ($var_name) { if ($candidate_if_types) { - $if_types[$var_name] = [['@' . json_encode($candidate_if_types[0])]]; + $if_types[$var_name] = [['@' . json_encode($candidate_if_types[0], JSON_THROW_ON_ERROR)]]; } else { $if_types[$var_name] = [['!falsy']]; } diff --git a/src/Psalm/Internal/Clause.php b/src/Psalm/Internal/Clause.php index 5497f579b9b..a3c6e70232f 100644 --- a/src/Psalm/Internal/Clause.php +++ b/src/Psalm/Internal/Clause.php @@ -19,6 +19,8 @@ use function strpos; use function substr; +use const JSON_THROW_ON_ERROR; + /** * @internal * @@ -110,7 +112,7 @@ public function __construct( sort($possibilities[$i]); } - $this->hash = md5((string) json_encode($possibilities)); + $this->hash = md5(json_encode($possibilities, JSON_THROW_ON_ERROR)); } } diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 2482dffd94a..7f46fafc157 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -73,6 +73,7 @@ use function version_compare; use const DIRECTORY_SEPARATOR; +use const JSON_THROW_ON_ERROR; use const LC_CTYPE; use const PHP_EOL; use const PHP_OS; @@ -742,7 +743,10 @@ private static function storeTypeMap(Providers $providers, Config $config, strin $expected_references ); - $type_map_string = json_encode(['files' => $name_file_map, 'references' => $reference_dictionary]); + $type_map_string = json_encode( + ['files' => $name_file_map, 'references' => $reference_dictionary], + JSON_THROW_ON_ERROR + ); $providers->file_provider->setContents( $type_map_location, diff --git a/src/Psalm/Internal/CliUtils.php b/src/Psalm/Internal/CliUtils.php index 37b7782be66..3c683f3895b 100644 --- a/src/Psalm/Internal/CliUtils.php +++ b/src/Psalm/Internal/CliUtils.php @@ -4,6 +4,7 @@ use Composer\Autoload\ClassLoader; use Composer\InstalledVersions; +use JsonException; use OutOfBoundsException; use Phar; use Psalm\Config; @@ -44,6 +45,7 @@ use function trim; use const DIRECTORY_SEPARATOR; +use const JSON_THROW_ON_ERROR; use const PHP_EOL; use const STDERR; use const STDIN; @@ -172,8 +174,17 @@ public static function getVendorDir(string $current_dir): string if (!file_exists($composer_json_path)) { return 'vendor'; } + try { + $composer_json = json_decode(file_get_contents($composer_json_path), true, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + fwrite( + STDERR, + 'Invalid composer.json at ' . $composer_json_path . "\n" . $e->getMessage() . "\n" + ); + exit(1); + } - if (!$composer_json = json_decode(file_get_contents($composer_json_path), true)) { + if (!$composer_json) { fwrite( STDERR, 'Invalid composer.json at ' . $composer_json_path . "\n" diff --git a/src/Psalm/Internal/Codebase/TaintFlowGraph.php b/src/Psalm/Internal/Codebase/TaintFlowGraph.php index f24a20e4634..f493bfab21b 100644 --- a/src/Psalm/Internal/Codebase/TaintFlowGraph.php +++ b/src/Psalm/Internal/Codebase/TaintFlowGraph.php @@ -41,6 +41,8 @@ use function strlen; use function substr; +use const JSON_THROW_ON_ERROR; + /** * @internal */ @@ -468,8 +470,8 @@ private function getChildNodes( $new_destination->path_types = array_merge($generated_source->path_types, [$path_type]); $key = $to_id . - ' ' . json_encode($new_destination->specialized_calls) . - ' ' . json_encode($new_destination->taints); + ' ' . json_encode($new_destination->specialized_calls, JSON_THROW_ON_ERROR) . + ' ' . json_encode($new_destination->taints, JSON_THROW_ON_ERROR); $new_sources[$key] = $new_destination; } diff --git a/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php b/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php index 6d7ab705dab..25d731dcc96 100644 --- a/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php +++ b/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php @@ -12,6 +12,8 @@ use function strpos; use function strtotime; +use const JSON_THROW_ON_ERROR; + /** * Environment variables collector for CI environment. * @@ -284,7 +286,7 @@ protected function fillGithubActions(): BuildInfoCollector if (isset($this->env['GITHUB_EVENT_PATH'])) { $event_json = file_get_contents((string) $this->env['GITHUB_EVENT_PATH']); /** @var array */ - $event_data = json_decode($event_json, true); + $event_data = json_decode($event_json, true, 512, JSON_THROW_ON_ERROR); if (isset($event_data['head_commit'])) { /** diff --git a/src/Psalm/Internal/PluginManager/PluginListFactory.php b/src/Psalm/Internal/PluginManager/PluginListFactory.php index 727362fe46f..6ae81a32b8d 100644 --- a/src/Psalm/Internal/PluginManager/PluginListFactory.php +++ b/src/Psalm/Internal/PluginManager/PluginListFactory.php @@ -11,6 +11,7 @@ use function urlencode; use const DIRECTORY_SEPARATOR; +use const JSON_THROW_ON_ERROR; /** * @internal @@ -71,7 +72,8 @@ private function findLockFiles(): array 'packages' => [], 'packages-dev' => [], ]; - $composer_lock_filenames[] = 'data:application/json,' . urlencode(json_encode($stub_composer_lock)); + $composer_lock_filenames[] = 'data:application/json,' + . urlencode(json_encode($stub_composer_lock, JSON_THROW_ON_ERROR)); } return $composer_lock_filenames; diff --git a/src/Psalm/Internal/Provider/ParserCacheProvider.php b/src/Psalm/Internal/Provider/ParserCacheProvider.php index 85c0c06c42c..1918279554e 100644 --- a/src/Psalm/Internal/Provider/ParserCacheProvider.php +++ b/src/Psalm/Internal/Provider/ParserCacheProvider.php @@ -2,6 +2,7 @@ namespace Psalm\Internal\Provider; +use JsonException; use PhpParser; use PhpParser\Node\Stmt; use Psalm\Config; @@ -31,6 +32,7 @@ use const DIRECTORY_SEPARATOR; use const E_USER_ERROR; +use const JSON_THROW_ON_ERROR; use const SCANDIR_SORT_NONE; /** @@ -193,7 +195,14 @@ private function getExistingFileContentHashes(): array return []; } - $hashes_decoded = json_decode($hashes_encoded, true); + try { + $hashes_decoded = json_decode($hashes_encoded, true, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + error_log('Failed to parse hashes: ' . $e->getMessage()); + $this->existing_file_content_hashes = []; + + return []; + } if (!is_array($hashes_decoded)) { error_log('Unexpected value ' . gettype($hashes_decoded)); @@ -281,7 +290,7 @@ public function saveFileContentHashes(): void file_put_contents( $file_hashes_path, - json_encode($file_content_hashes) + json_encode($file_content_hashes, JSON_THROW_ON_ERROR) ); } diff --git a/src/Psalm/Plugin/Shepherd.php b/src/Psalm/Plugin/Shepherd.php index 51748d5f61a..93192df66b6 100644 --- a/src/Psalm/Plugin/Shepherd.php +++ b/src/Psalm/Plugin/Shepherd.php @@ -29,6 +29,7 @@ use const CURLOPT_POST; use const CURLOPT_POSTFIELDS; use const CURLOPT_RETURNTRANSFER; +use const JSON_THROW_ON_ERROR; use const PHP_EOL; use const PHP_URL_SCHEME; use const STDERR; @@ -76,7 +77,7 @@ static function (IssueData $i): bool { 'level' => Config::getInstance()->level ]; - $payload = json_encode($data); + $payload = json_encode($data, JSON_THROW_ON_ERROR); $base_address = $codebase->config->shepherd_host; diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index be5e2e2e146..4a0bcdbf761 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -57,6 +57,8 @@ use function strtolower; use function substr; +use const JSON_THROW_ON_ERROR; + class Reconciler { public const RECONCILIATION_OK = 0; @@ -190,11 +192,11 @@ public static function reconcileKeyedTypes( $nested_negated = !$negated; /** @var array>> */ - $data = json_decode(substr($new_type_part_part, 2), true); + $data = json_decode(substr($new_type_part_part, 2), true, 512, JSON_THROW_ON_ERROR); } else { $nested_negated = $negated; /** @var array>> */ - $data = json_decode(substr($new_type_part_part, 1), true); + $data = json_decode(substr($new_type_part_part, 1), true, 512, JSON_THROW_ON_ERROR); } $existing_types = self::reconcileKeyedTypes( diff --git a/tests/ComposerLockTest.php b/tests/ComposerLockTest.php index cf856d4a206..fc46ccefe33 100644 --- a/tests/ComposerLockTest.php +++ b/tests/ComposerLockTest.php @@ -7,6 +7,8 @@ use function json_encode; +use const JSON_THROW_ON_ERROR; + /** @group PluginManager */ class ComposerLockTest extends TestCase { @@ -216,6 +218,6 @@ private function pluginEntry(string $package_name, string $package_class): array */ private function jsonFile($data): string { - return 'data:application/json,' . json_encode($data); + return 'data:application/json,' . json_encode($data, JSON_THROW_ON_ERROR); } } diff --git a/tests/ReportOutputTest.php b/tests/ReportOutputTest.php index 5fd7b59bc47..5fb82b594f4 100644 --- a/tests/ReportOutputTest.php +++ b/tests/ReportOutputTest.php @@ -24,6 +24,8 @@ use function preg_replace; use function unlink; +use const JSON_THROW_ON_ERROR; + class ReportOutputTest extends TestCase { public function setUp(): void @@ -666,7 +668,7 @@ public function testSarifReport(): void $this->assertSame( $issue_data, - json_decode(IssueBuffer::getOutput(IssueBuffer::getIssuesData(), $sarif_report_options), true) + json_decode(IssueBuffer::getOutput(IssueBuffer::getIssuesData(), $sarif_report_options), true, 512, JSON_THROW_ON_ERROR) ); } @@ -819,7 +821,7 @@ public function testJsonReport(): void $this->assertSame( $issue_data, - json_decode(IssueBuffer::getOutput(IssueBuffer::getIssuesData(), $json_report_options), true) + json_decode(IssueBuffer::getOutput(IssueBuffer::getIssuesData(), $json_report_options), true, 512, JSON_THROW_ON_ERROR) ); } @@ -853,7 +855,7 @@ public function testFilteredJsonReportIsStillArray(): void $fixable_issue_counts, $report_options ); - $this->assertIsArray(json_decode($report->create())); + $this->assertIsArray(json_decode($report->create(), null, 512, JSON_THROW_ON_ERROR)); } public function testSonarqubeReport(): void @@ -950,7 +952,7 @@ public function testSonarqubeReport(): void $this->assertSame( $issue_data, - json_decode(IssueBuffer::getOutput(IssueBuffer::getIssuesData(), $sonarqube_report_options), true) + json_decode(IssueBuffer::getOutput(IssueBuffer::getIssuesData(), $sonarqube_report_options), true, 512, JSON_THROW_ON_ERROR) ); } From 83f1d5252820dbc2edc8534e1b4dfcebc860b009 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 5 Jan 2022 23:29:18 +0200 Subject: [PATCH 63/78] Applied ListToArrayDestructRector --- src/Psalm/Internal/PhpTraverser/CustomTraverser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Internal/PhpTraverser/CustomTraverser.php b/src/Psalm/Internal/PhpTraverser/CustomTraverser.php index 2c96c6b1c9f..2f8f6a44d1f 100644 --- a/src/Psalm/Internal/PhpTraverser/CustomTraverser.php +++ b/src/Psalm/Internal/PhpTraverser/CustomTraverser.php @@ -164,7 +164,7 @@ protected function traverseArray(array $nodes): array } if (!empty($doNodes)) { - while (list($i, $replace) = array_pop($doNodes)) { + while ([$i, $replace] = array_pop($doNodes)) { array_splice($nodes, $i, 1, $replace); } } From e4c16c10e788909cbb654b3ca0f2a8fd2df00c7f Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Wed, 5 Jan 2022 23:32:01 +0200 Subject: [PATCH 64/78] Enforce `[...]` usage instead of `list(...)` --- phpcs.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/phpcs.xml b/phpcs.xml index f1c27e858ca..4933b1dce17 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -216,4 +216,10 @@ + + + From 030195a395e8e7f3f1147ed866fe834c3b51a9f0 Mon Sep 17 00:00:00 2001 From: orklah Date: Tue, 4 Jan 2022 10:48:29 +0100 Subject: [PATCH 65/78] parse array{} into an empty array --- src/Psalm/Internal/Type/ParseTreeCreator.php | 7 +++++++ src/Psalm/Internal/Type/TypeParser.php | 4 ++-- tests/TypeParseTest.php | 8 ++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Type/ParseTreeCreator.php b/src/Psalm/Internal/Type/ParseTreeCreator.php index edad53cb928..15256e5dedc 100644 --- a/src/Psalm/Internal/Type/ParseTreeCreator.php +++ b/src/Psalm/Internal/Type/ParseTreeCreator.php @@ -751,6 +751,13 @@ private function handleValue(array $type_token): void $new_parent ); ++$this->t; + + $nexter_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null; + + if ($nexter_token !== null && $nexter_token[0] === '}') { + $new_leaf->terminated = true; + ++$this->t; + } break; case '(': diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index 26e2658ca4b..d7b9f8268e6 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -1235,7 +1235,7 @@ private static function getTypeFromIndexAccessTree( /** * @param array> $template_type_map * @param array $type_aliases - * @return TCallableKeyedArray|TKeyedArray|TObjectWithProperties + * @return TCallableKeyedArray|TKeyedArray|TObjectWithProperties|TArray * @throws TypeParseTreeException */ private static function getTypeFromKeyedArrayTree( @@ -1314,7 +1314,7 @@ private static function getTypeFromKeyedArrayTree( } if (!$properties) { - throw new TypeParseTreeException('No properties supplied for TKeyedArray'); + return new TArray([Type::getNever(), Type::getNever()]); } if ($type === 'object') { diff --git a/tests/TypeParseTest.php b/tests/TypeParseTest.php index 30ea08a39a0..d2f29297ecc 100644 --- a/tests/TypeParseTest.php +++ b/tests/TypeParseTest.php @@ -893,6 +893,14 @@ public function testSingleLiteralString(): void ); } + public function testEmptyArrayShape(): void + { + $this->assertSame( + 'array', + (string)Type::parseString('array{}') + ); + } + public function testSingleLiteralInt(): void { $this->assertSame( From 9d3a51db6a06a7a5ffc8efa95a17db44f47211dd Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 5 Jan 2022 23:02:35 +0100 Subject: [PATCH 66/78] remove obsolete test and fix a missing exception --- src/Psalm/Internal/Type/ParseTreeCreator.php | 3 +++ tests/AnnotationTest.php | 8 -------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Psalm/Internal/Type/ParseTreeCreator.php b/src/Psalm/Internal/Type/ParseTreeCreator.php index 15256e5dedc..0ee5d9b329f 100644 --- a/src/Psalm/Internal/Type/ParseTreeCreator.php +++ b/src/Psalm/Internal/Type/ParseTreeCreator.php @@ -757,7 +757,10 @@ private function handleValue(array $type_token): void if ($nexter_token !== null && $nexter_token[0] === '}') { $new_leaf->terminated = true; ++$this->t; + } elseif ($nexter_token === null) { + throw new TypeParseTreeException('Unclosed bracket in keyed array'); } + break; case '(': diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index 2f7ec306d08..506524aa215 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -1490,14 +1490,6 @@ public function bar() { ', 'error_message' => 'UndefinedDocblockClass', ], - 'preventBadTKeyedArrayFormat' => [ - ' 'InvalidDocblock', - ], 'noPhpStormAnnotationsThankYou' => [ ' Date: Wed, 5 Jan 2022 23:14:54 +0100 Subject: [PATCH 67/78] replace `array` as a way to detect empty arrays by a dedicated method --- src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php | 2 +- .../Analyzer/Statements/Expression/AssignmentAnalyzer.php | 2 +- src/Psalm/Internal/Type/SimpleAssertionReconciler.php | 2 +- .../Internal/Type/SimpleNegatedAssertionReconciler.php | 2 +- src/Psalm/Type/Atomic.php | 2 +- src/Psalm/Type/Union.php | 8 ++++++++ 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 864c3c81087..148d109079a 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -503,7 +503,7 @@ public function analyze( && !$inferred_return_type->isSingleIntLiteral() && !$inferred_return_type->isSingleStringLiteral() && !$inferred_return_type->isTrue() - && $inferred_return_type->getId() !== 'array' + && !$inferred_return_type->isEmptyArray() ) { $manipulator->makePure(); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 9f9814669fc..182fdbb387e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -1024,7 +1024,7 @@ public static function assignByRefParam( $by_ref_out_type->parent_nodes += $existing_type->parent_nodes; } - if ($existing_type->getId() !== 'array') { + if (!$existing_type->isEmptyArray()) { $context->vars_in_scope[$var_id] = $by_ref_out_type; if (!($stmt_type = $statements_analyzer->node_data->getType($stmt)) diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index a23020d5116..e340d336798 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -554,7 +554,7 @@ private static function reconcileNonEmptyCountable( if (!$array_atomic_type instanceof TNonEmptyArray || ($array_atomic_type->count < $min_count) ) { - if ($array_atomic_type->getId() === 'array') { + if (!$array_atomic_type->isEmptyArray()) { $existing_var_type->removeType('array'); } else { $non_empty_array = new TNonEmptyArray( diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index 87eb7fc8834..58d31f9baab 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -486,7 +486,7 @@ private static function reconcileNonEmptyCountable( $did_remove_type = true; $existing_var_type->removeType('array'); - } elseif ($array_atomic_type->getId() !== 'array') { + } elseif (!$array_atomic_type->isEmptyArray()) { $did_remove_type = true; if (!$min_count) { diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index e548df58219..a4c266d2d79 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -785,7 +785,7 @@ public function isFalsy(): bool return true; } - if ($this instanceof TArray && $this->getId() === 'array') { + if ($this instanceof TArray && $this->isEmptyArray()) { return true; } diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 843fd742dd7..c0f82a5cec3 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -1598,4 +1598,12 @@ public function getSingleAtomic(): Atomic { return reset($this->types); } + + public function isEmptyArray(): bool + { + return count($this->types) === 1 + && isset($this->types['array']) + && $this->types['array'] instanceof TArray + && $this->types['array']->isEmptyArray(); + } } From ee99deaa50dd9e4e18e0767c16239f524bdab0f1 Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 5 Jan 2022 23:28:08 +0100 Subject: [PATCH 68/78] fix a case not always on TArray --- src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index 58d31f9baab..72b5a11488d 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -486,7 +486,7 @@ private static function reconcileNonEmptyCountable( $did_remove_type = true; $existing_var_type->removeType('array'); - } elseif (!$array_atomic_type->isEmptyArray()) { + } elseif (!($array_atomic_type instanceof TArray && $array_atomic_type->isEmptyArray())) { $did_remove_type = true; if (!$min_count) { From 7f40489202a228dd27b2126be6d2c74a81fa9364 Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 5 Jan 2022 23:41:56 +0100 Subject: [PATCH 69/78] fix inverted condition --- src/Psalm/Internal/Type/SimpleAssertionReconciler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index e340d336798..baf3479524c 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -554,7 +554,7 @@ private static function reconcileNonEmptyCountable( if (!$array_atomic_type instanceof TNonEmptyArray || ($array_atomic_type->count < $min_count) ) { - if (!$array_atomic_type->isEmptyArray()) { + if ($array_atomic_type->isEmptyArray()) { $existing_var_type->removeType('array'); } else { $non_empty_array = new TNonEmptyArray( From 8726065d21a5576b6a09c6102c1bdea6348b6fea Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Thu, 6 Jan 2022 00:45:11 +0200 Subject: [PATCH 70/78] Applied ClosureToArrowFunctionRector --- examples/plugins/ClassUnqualifier.php | 4 +- phpcs.xml | 11 +++ src/Psalm/Config.php | 5 +- src/Psalm/Config/Creator.php | 4 +- src/Psalm/Config/FileFilter.php | 4 +- src/Psalm/Config/IssueHandler.php | 32 +++---- src/Psalm/ErrorBaseline.php | 35 +++---- src/Psalm/Internal/Algebra.php | 12 +-- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 12 +-- .../FunctionLike/ReturnTypeAnalyzer.php | 4 +- src/Psalm/Internal/Analyzer/ScopeAnalyzer.php | 40 ++------ .../Statements/Block/ForeachAnalyzer.php | 4 +- .../Block/IfConditionalAnalyzer.php | 12 +-- .../Block/IfElse/ElseIfAnalyzer.php | 12 +-- .../Statements/Block/IfElseAnalyzer.php | 12 +-- .../Expression/AssignmentAnalyzer.php | 8 +- .../Expression/BinaryOp/AndAnalyzer.php | 8 +- .../Expression/BinaryOp/OrAnalyzer.php | 12 +-- .../Expression/Call/ArgumentsAnalyzer.php | 18 ++-- .../Call/ArrayFunctionArgumentsAnalyzer.php | 4 +- .../Expression/Call/FunctionCallAnalyzer.php | 14 +-- .../ExistingAtomicMethodCallAnalyzer.php | 24 ++--- .../Method/MethodCallReturnTypeFetcher.php | 8 +- .../Call/Method/MissingMethodCallHandler.php | 14 ++- .../Expression/Call/MethodCallAnalyzer.php | 4 +- .../Call/NamedFunctionCallHandler.php | 4 +- .../Expression/Call/NewAnalyzer.php | 30 ++---- .../StaticMethod/AtomicStaticCallAnalyzer.php | 21 ++--- .../ExistingAtomicStaticCallAnalyzer.php | 10 +- .../Statements/Expression/CallAnalyzer.php | 26 ++--- .../Expression/Fetch/ArrayFetchAnalyzer.php | 4 +- .../Statements/Expression/MatchAnalyzer.php | 15 ++- .../Statements/Expression/TernaryAnalyzer.php | 4 +- src/Psalm/Internal/Cli/LanguageServer.php | 5 +- src/Psalm/Internal/Cli/Psalm.php | 31 +++--- src/Psalm/Internal/Cli/Psalter.php | 5 +- src/Psalm/Internal/Cli/Refactor.php | 5 +- src/Psalm/Internal/Codebase/Analyzer.php | 4 +- src/Psalm/Internal/Codebase/ClassLikes.php | 18 ++-- src/Psalm/Internal/Codebase/Populator.php | 14 +-- src/Psalm/Internal/Codebase/Reflection.php | 4 +- src/Psalm/Internal/Codebase/Scanner.php | 8 +- .../Internal/Codebase/TaintFlowGraph.php | 4 +- src/Psalm/Internal/Fork/PsalmRestarter.php | 4 +- .../Internal/PhpVisitor/CloningVisitor.php | 4 +- .../Reflector/ClassLikeNodeScanner.php | 4 +- .../Reflector/FunctionLikeDocblockScanner.php | 8 +- .../PluginManager/Command/ShowCommand.php | 4 +- .../Provider/FileReferenceProvider.php | 4 +- .../ArrayFilterReturnTypeProvider.php | 4 +- .../ArrayMapReturnTypeProvider.php | 4 +- .../MinMaxReturnTypeProvider.php | 8 +- .../Internal/Provider/StatementsProvider.php | 12 +-- .../Type/Comparator/ArrayTypeComparator.php | 4 +- src/Psalm/Internal/Type/TemplateResult.php | 12 +-- .../Type/TemplateStandinTypeReplacer.php | 4 +- src/Psalm/Internal/Type/TypeCombiner.php | 12 +-- src/Psalm/Internal/Type/TypeExpander.php | 22 ++--- src/Psalm/Internal/Type/TypeParser.php | 4 +- src/Psalm/Plugin/Shepherd.php | 4 +- src/Psalm/Report.php | 4 +- src/Psalm/Report/CodeClimateReport.php | 45 +++------ src/Psalm/Report/XmlReport.php | 8 +- src/Psalm/Storage/Assertion.php | 4 +- src/Psalm/Storage/FunctionLikeStorage.php | 22 +++-- src/Psalm/Type/Atomic.php | 16 +--- src/Psalm/Type/Atomic/GenericTrait.php | 14 +-- .../Type/Atomic/HasIntersectionTrait.php | 11 +-- src/Psalm/Type/Atomic/TNamedObject.php | 4 +- .../Type/Atomic/TObjectWithProperties.php | 34 ++----- src/Psalm/Type/Atomic/TTemplateParam.php | 4 +- src/Psalm/Type/Atomic/TTypeAlias.php | 4 +- src/Psalm/Type/Union.php | 94 +++++++------------ tests/CodebaseTest.php | 4 +- tests/Config/ConfigTest.php | 4 +- tests/DocumentationTest.php | 4 +- tests/FileDiffTest.php | 8 +- tests/TaintTest.php | 4 +- tests/TestCase.php | 4 +- tests/TypeComparatorTest.php | 4 +- 80 files changed, 314 insertions(+), 629 deletions(-) diff --git a/examples/plugins/ClassUnqualifier.php b/examples/plugins/ClassUnqualifier.php index be5555e82b5..bc84b0e21ac 100644 --- a/examples/plugins/ClassUnqualifier.php +++ b/examples/plugins/ClassUnqualifier.php @@ -41,9 +41,7 @@ public static function afterClassLikeExistenceCheck( $new_candidate_type = implode( '', array_map( - function ($f) { - return $f[0]; - }, + fn($f) => $f[0], $type_tokens ) ); diff --git a/phpcs.xml b/phpcs.xml index 4933b1dce17..b1f79cf0c44 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -222,4 +222,15 @@ https://github.com/slevomat/coding-standard/#slevomatcodingstandardphpshortlist- --> + + + + + + + + diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 6f679023e83..cf95806baf4 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -2080,13 +2080,12 @@ public function visitComposerAutoloadFiles(ProjectAnalyzer $project_analyzer, ?P if (file_exists($vendor_autoload_files_path)) { $this->include_collector->runAndCollect( - function () use ($vendor_autoload_files_path) { + fn(): array => /** * @psalm-suppress UnresolvableInclude * @var string[] */ - return require $vendor_autoload_files_path; - } + require $vendor_autoload_files_path ); } diff --git a/src/Psalm/Config/Creator.php b/src/Psalm/Config/Creator.php index bf837b666e4..dca4c6e470e 100644 --- a/src/Psalm/Config/Creator.php +++ b/src/Psalm/Config/Creator.php @@ -136,9 +136,7 @@ public static function getLevel(array $issues, int $counted_types): int // remove any issues where < 0.1% of expressions are affected $filtered_issues = array_filter( $issues, - function ($amount): bool { - return $amount > 0.1; - } + fn($amount): bool => $amount > 0.1 ); if (array_sum($filtered_issues) > 0.5) { diff --git a/src/Psalm/Config/FileFilter.php b/src/Psalm/Config/FileFilter.php index 1443594c7a7..90c7606212d 100644 --- a/src/Psalm/Config/FileFilter.php +++ b/src/Psalm/Config/FileFilter.php @@ -406,9 +406,7 @@ public static function loadFromXMLElement( private static function isRegularExpression(string $string): bool { set_error_handler( - function (): bool { - return false; - }, + fn(): bool => false, E_WARNING ); $is_regexp = preg_match($string, '') !== false; diff --git a/src/Psalm/Config/IssueHandler.php b/src/Psalm/Config/IssueHandler.php index cdc2c288289..d3b6a3cdf0a 100644 --- a/src/Psalm/Config/IssueHandler.php +++ b/src/Psalm/Config/IssueHandler.php @@ -149,26 +149,22 @@ public static function getAllIssueTypes(): array { return array_filter( array_map( - function (string $file_name): string { - return substr($file_name, 0, -4); - }, + fn(string $file_name): string => substr($file_name, 0, -4), scandir(dirname(__DIR__) . '/Issue', SCANDIR_SORT_NONE) ), - function (string $issue_name): bool { - return $issue_name !== '' - && $issue_name !== 'MethodIssue' - && $issue_name !== 'PropertyIssue' - && $issue_name !== 'FunctionIssue' - && $issue_name !== 'ArgumentIssue' - && $issue_name !== 'VariableIssue' - && $issue_name !== 'ClassIssue' - && $issue_name !== 'CodeIssue' - && $issue_name !== 'PsalmInternalError' - && $issue_name !== 'ParseError' - && $issue_name !== 'PluginIssue' - && $issue_name !== 'MixedIssue' - && $issue_name !== 'MixedIssueTrait'; - } + fn(string $issue_name): bool => $issue_name !== '' + && $issue_name !== 'MethodIssue' + && $issue_name !== 'PropertyIssue' + && $issue_name !== 'FunctionIssue' + && $issue_name !== 'ArgumentIssue' + && $issue_name !== 'VariableIssue' + && $issue_name !== 'ClassIssue' + && $issue_name !== 'CodeIssue' + && $issue_name !== 'PsalmInternalError' + && $issue_name !== 'ParseError' + && $issue_name !== 'PluginIssue' + && $issue_name !== 'MixedIssue' + && $issue_name !== 'MixedIssueTrait' ); } } diff --git a/src/Psalm/ErrorBaseline.php b/src/Psalm/ErrorBaseline.php index 41235a317bc..d0cc5ddd3e1 100644 --- a/src/Psalm/ErrorBaseline.php +++ b/src/Psalm/ErrorBaseline.php @@ -48,9 +48,7 @@ public static function countTotalIssues(array $existingIssues): int /** * @param array{o:int, s:array} $existingIssue */ - function (int $carry, array $existingIssue): int { - return $carry + $existingIssue['o']; - }, + fn(int $carry, array $existingIssue): int => $carry + $existingIssue['o'], 0 ); } @@ -260,9 +258,7 @@ private static function writeToFile( ('php:' . PHP_VERSION), ], array_map( - function (string $extension): string { - return $extension . ':' . phpversion($extension); - }, + fn(string $extension): string => $extension . ':' . phpversion($extension), $extensions ) ))); @@ -299,21 +295,18 @@ function (string $extension): string { /** * @param string[] $matches */ - function (array $matches): string { - return - ' 'saveXML() ); diff --git a/src/Psalm/Internal/Algebra.php b/src/Psalm/Internal/Algebra.php index a622de7d437..d6ff1b7bde1 100644 --- a/src/Psalm/Internal/Algebra.php +++ b/src/Psalm/Internal/Algebra.php @@ -200,9 +200,7 @@ public static function simplifyCNF(array $clauses): array $clause_var_possibilities = array_values( array_filter( $clause_b->possibilities[$clause_var], - function (string $possible_type) use ($negated_clause_type): bool { - return $possible_type !== $negated_clause_type; - } + fn(string $possible_type): bool => $possible_type !== $negated_clause_type ) ); @@ -307,9 +305,7 @@ public static function getTruthsFromFormula( // if there's only one active clause, return all the non-negation clause members ORed together $things_that_can_be_said = array_filter( $possible_types, - function (string $possible_type): bool { - return $possible_type[0] !== '!'; - } + fn(string $possible_type): bool => $possible_type[0] !== '!' ); if ($things_that_can_be_said && count($things_that_can_be_said) === count($possible_types)) { @@ -595,9 +591,7 @@ public static function negateFormula(array $clauses): array { $clauses = array_filter( $clauses, - function ($clause) { - return $clause->reconcilable; - } + fn($clause) => $clause->reconcilable ); if (!$clauses) { diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 0726a3ea3e9..fe237e676f8 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -898,11 +898,9 @@ public static function addContextProperties( if ($property_type_location && !$fleshed_out_type->isMixed()) { $stmt = array_filter( $stmts, - function ($stmt) use ($property_name): bool { - return $stmt instanceof PhpParser\Node\Stmt\Property - && isset($stmt->props[0]->name->name) - && $stmt->props[0]->name->name === $property_name; - } + fn($stmt): bool => $stmt instanceof PhpParser\Node\Stmt\Property + && isset($stmt->props[0]->name->name) + && $stmt->props[0]->name->name === $property_name ); $suppressed = []; @@ -1948,9 +1946,7 @@ public static function analyzeClassMethodReturnType( } $overridden_method_ids = array_map( - function ($method_id) { - return $method_id->__toString(); - }, + fn($method_id) => $method_id->__toString(), $overridden_method_ids ); diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php index 1512bf9d64f..3203c93b34e 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php @@ -239,9 +239,7 @@ public static function verifyReturnType( if ($number_of_types > 1) { $inferred_return_type_parts = array_filter( $inferred_return_type_parts, - static function (Union $union_type): bool { - return !$union_type->isNever(); - } + static fn(Union $union_type): bool => !$union_type->isNever() ); } diff --git a/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php b/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php index 9fa0567f79c..700677ca252 100644 --- a/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php @@ -151,9 +151,7 @@ public static function getControlActions( $all_leave = !array_filter( $if_statement_actions, - function ($action) { - return $action === self::ACTION_NONE; - } + fn($action) => $action === self::ACTION_NONE ); $else_statement_actions = $stmt->else @@ -168,9 +166,7 @@ function ($action) { && $else_statement_actions && !array_filter( $else_statement_actions, - function ($action) { - return $action === self::ACTION_NONE; - } + fn($action) => $action === self::ACTION_NONE ); $all_elseif_actions = []; @@ -187,9 +183,7 @@ function ($action) { $all_leave = $all_leave && !array_filter( $elseif_control_actions, - function ($action) { - return $action === self::ACTION_NONE; - } + fn($action) => $action === self::ACTION_NONE ); $all_elseif_actions = array_merge($elseif_control_actions, $all_elseif_actions); @@ -216,9 +210,7 @@ function ($action) { $else_statement_actions, $all_elseif_actions ), - function ($action) { - return $action !== self::ACTION_NONE; - } + fn($action) => $action !== self::ACTION_NONE ); } @@ -278,9 +270,7 @@ function ($action) { $all_case_actions = array_filter( $all_case_actions, - function ($action) { - return $action !== self::ACTION_NONE; - } + fn($action) => $action !== self::ACTION_NONE ); if ($has_default_terminator || $stmt->getAttribute('allMatched', false)) { @@ -307,9 +297,7 @@ function ($action) { $control_actions = array_filter( array_merge($control_actions, $loop_actions), - function ($action) { - return $action !== self::ACTION_NONE; - } + fn($action) => $action !== self::ACTION_NONE ); if ($stmt instanceof PhpParser\Node\Stmt\While_ @@ -365,9 +353,7 @@ function ($action) { $try_leaves = !array_filter( $try_statement_actions, - function ($action) { - return $action === self::ACTION_NONE; - } + fn($action) => $action === self::ACTION_NONE ); $all_catch_actions = []; @@ -386,9 +372,7 @@ function ($action) { $all_leave = $all_leave && !array_filter( $catch_actions, - function ($action) { - return $action === self::ACTION_NONE; - } + fn($action) => $action === self::ACTION_NONE ); if (!$all_leave) { @@ -425,9 +409,7 @@ function ($action) { return array_merge( array_filter( $control_actions, - function ($action) { - return $action !== self::ACTION_NONE; - } + fn($action) => $action !== self::ACTION_NONE ), $finally_statement_actions ); @@ -436,9 +418,7 @@ function ($action) { $control_actions = array_filter( array_merge($control_actions, $try_statement_actions), - function ($action) { - return $action !== self::ACTION_NONE; - } + fn($action) => $action !== self::ACTION_NONE ); } } diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index 2c7fe74f9c6..f03f3d0a0b5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -1000,9 +1000,7 @@ public static function getKeyValueParamsForTraversableObject( : array_values( array_map( /** @param array $arr */ - function (array $arr) use ($iterator_atomic_type): Union { - return $arr[$iterator_atomic_type->value] ?? Type::getMixed(); - }, + fn(array $arr): Union => $arr[$iterator_atomic_type->value] ?? Type::getMixed(), $generic_storage->template_types ) ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php index 79c29b24442..eb6ecfb1d0f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php @@ -76,11 +76,9 @@ public static function analyze( $entry_clauses = array_values( array_filter( $entry_clauses, - function (Clause $c) use ($changed_var_ids): bool { - return count($c->possibilities) > 1 - || $c->wedge - || !isset($changed_var_ids[array_keys($c->possibilities)[0]]); - } + fn(Clause $c): bool => count($c->possibilities) > 1 + || $c->wedge + || !isset($changed_var_ids[array_keys($c->possibilities)[0]]) ) ); } @@ -209,9 +207,7 @@ function (Clause $c) use ($changed_var_ids): bool { * * @return true */ - function (Union $_): bool { - return true; - }, + fn(Union $_): bool => true, array_diff_key( $if_conditional_context->vars_in_scope, $pre_condition_vars_in_scope, diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php index 0f95381a6b7..bac5b65fd84 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php @@ -153,9 +153,7 @@ function (Clause $c) use ($assigned_in_conditional_var_ids, $elseif_cond_id): Cl $elseif_context_clauses = array_values( array_filter( $elseif_context_clauses, - function ($c) use ($reconciled_expression_clauses): bool { - return !in_array($c->hash, $reconciled_expression_clauses); - } + fn($c): bool => !in_array($c->hash, $reconciled_expression_clauses) ) ); } @@ -167,9 +165,7 @@ function ($c) use ($reconciled_expression_clauses): bool { try { if (array_filter( $entry_clauses, - function ($clause): bool { - return (bool)$clause->possibilities; - } + fn($clause): bool => (bool)$clause->possibilities )) { $omit_keys = array_reduce( $entry_clauses, @@ -177,9 +173,7 @@ function ($clause): bool { * @param array $carry * @return array */ - function (array $carry, Clause $clause): array { - return array_merge($carry, array_keys($clause->possibilities)); - }, + fn(array $carry, Clause $clause): array => array_merge($carry, array_keys($clause->possibilities)), [] ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php index 7418e43ce61..221ab6ca49d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php @@ -186,9 +186,7 @@ function (Clause $c) use ($mixed_var_ids, $cond_object_id): Clause { $if_context->clauses = array_values( array_filter( $if_context->clauses, - function ($c) use ($reconciled_expression_clauses): bool { - return !in_array($c->hash, $reconciled_expression_clauses); - } + fn($c): bool => !in_array($c->hash, $reconciled_expression_clauses) ) ); @@ -239,9 +237,7 @@ function ($c) use ($reconciled_expression_clauses): bool { if (array_filter( $context->clauses, - function ($clause): bool { - return (bool)$clause->possibilities; - } + fn($clause): bool => (bool)$clause->possibilities )) { $omit_keys = array_reduce( $context->clauses, @@ -249,9 +245,7 @@ function ($clause): bool { * @param array $carry * @return array */ - function (array $carry, Clause $clause): array { - return array_merge($carry, array_keys($clause->possibilities)); - }, + fn(array $carry, Clause $clause): array => array_merge($carry, array_keys($clause->possibilities)), [] ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 9f9814669fc..3ec11c6d2d5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -739,16 +739,12 @@ private static function taintAssignment( $unspecialized_parent_nodes = array_filter( $parent_nodes, - function ($parent_node) { - return !$parent_node->specialization_key; - } + fn($parent_node) => !$parent_node->specialization_key ); $specialized_parent_nodes = array_filter( $parent_nodes, - function ($parent_node) { - return (bool) $parent_node->specialization_key; - } + fn($parent_node) => (bool) $parent_node->specialization_key ); $new_parent_nodes = []; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php index 1f4c28f1b42..df0c64194db 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php @@ -104,9 +104,7 @@ public static function analyze( $context_clauses = array_values( array_filter( $context_clauses, - function ($c) use ($reconciled_expression_clauses): bool { - return !in_array($c->hash, $reconciled_expression_clauses); - } + fn($c): bool => !in_array($c->hash, $reconciled_expression_clauses) ) ); @@ -210,9 +208,7 @@ function ($c) use ($reconciled_expression_clauses): bool { $if_context->reconciled_expression_clauses = array_merge( $if_context->reconciled_expression_clauses, array_map( - function ($c) { - return $c->hash; - }, + fn($c) => $c->hash, $partitioned_clauses[1] ) ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php index d584d43f69d..d6d675b522c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php @@ -167,9 +167,7 @@ public static function analyze( $negated_left_clauses = array_values( array_filter( $negated_left_clauses, - function ($c) use ($reconciled_expression_clauses): bool { - return !in_array($c->hash, $reconciled_expression_clauses); - } + fn($c): bool => !in_array($c->hash, $reconciled_expression_clauses) ) ); @@ -240,9 +238,7 @@ function ($c) use ($reconciled_expression_clauses): bool { $right_context->reconciled_expression_clauses = array_merge( $context->reconciled_expression_clauses, array_map( - function ($c) { - return $c->hash; - }, + fn($c) => $c->hash, $partitioned_clauses[1] ) ); @@ -252,9 +248,7 @@ function ($c) { $context->reconciled_expression_clauses = array_merge( $context->reconciled_expression_clauses, array_map( - function ($c) { - return $c->hash; - }, + fn($c) => $c->hash, $partitioned_clauses[1] ) ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php index a585baf22f3..f068d0dc4c6 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php @@ -358,17 +358,13 @@ private static function handleClosureArg( $replace_template_result = new TemplateResult( array_map( - function ($template_map) use ($codebase) { - return array_map( - function ($lower_bounds) use ($codebase) { - return TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds( - $lower_bounds, - $codebase - ); - }, - $template_map - ); - }, + fn($template_map) => array_map( + fn($lower_bounds) => TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds( + $lower_bounds, + $codebase + ), + $template_map + ), $template_result->lower_bounds ), [] diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php index 656e64dcbef..81517036547 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php @@ -147,9 +147,7 @@ public static function handleAddition( $unpacked_args = array_filter( $args, - function ($arg) { - return $arg->unpack; - } + fn($arg) => $arg->unpack ); if ($method_id === 'array_push' && !$unpacked_args) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php index becd4b5ea11..f0cfe0b6878 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php @@ -362,9 +362,8 @@ public static function analyze( $statements_analyzer->node_data->setIfTrueAssertions( $stmt, array_map( - function (Assertion $assertion) use ($inferred_lower_bounds, $codebase): Assertion { - return $assertion->getUntemplatedCopy($inferred_lower_bounds ?: [], null, $codebase); - }, + fn(Assertion $assertion): Assertion => + $assertion->getUntemplatedCopy($inferred_lower_bounds ?: [], null, $codebase), $function_call_info->function_storage->if_true_assertions ) ); @@ -374,9 +373,8 @@ function (Assertion $assertion) use ($inferred_lower_bounds, $codebase): Asserti $statements_analyzer->node_data->setIfFalseAssertions( $stmt, array_map( - function (Assertion $assertion) use ($inferred_lower_bounds, $codebase): Assertion { - return $assertion->getUntemplatedCopy($inferred_lower_bounds ?: [], null, $codebase); - }, + fn(Assertion $assertion): Assertion => + $assertion->getUntemplatedCopy($inferred_lower_bounds ?: [], null, $codebase), $function_call_info->function_storage->if_false_assertions ) ); @@ -935,9 +933,7 @@ private static function processAssertFunctionEffects( $context->vars_in_scope, $changed_var_ids, array_map( - function ($_): bool { - return true; - }, + fn($_): bool => true, $assert_type_assertions ), $statements_analyzer, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php index 439bce19bf8..3f559414e1e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php @@ -418,17 +418,11 @@ public static function analyze( $statements_analyzer->node_data->setIfTrueAssertions( $stmt, array_map( - function (Assertion $assertion) use ( - $class_template_params, + fn(Assertion $assertion): Assertion => $assertion->getUntemplatedCopy( + $class_template_params ?: [], $lhs_var_id, $codebase - ): Assertion { - return $assertion->getUntemplatedCopy( - $class_template_params ?: [], - $lhs_var_id, - $codebase - ); - }, + ), $method_storage->if_true_assertions ) ); @@ -438,17 +432,11 @@ function (Assertion $assertion) use ( $statements_analyzer->node_data->setIfFalseAssertions( $stmt, array_map( - function (Assertion $assertion) use ( - $class_template_params, + fn(Assertion $assertion): Assertion => $assertion->getUntemplatedCopy( + $class_template_params ?: [], $lhs_var_id, $codebase - ): Assertion { - return $assertion->getUntemplatedCopy( - $class_template_params ?: [], - $lhs_var_id, - $codebase - ); - }, + ), $method_storage->if_false_assertions ) ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php index b7fee9484b0..2ef9cd6fbba 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php @@ -314,16 +314,12 @@ public static function taintMethodCallResult( $unspecialized_parent_nodes = array_filter( $parent_nodes, - function ($parent_node) { - return !$parent_node->specialization_key; - } + fn($parent_node) => !$parent_node->specialization_key ); $specialized_parent_nodes = array_filter( $parent_nodes, - function ($parent_node) { - return (bool) $parent_node->specialization_key; - } + fn($parent_node) => (bool) $parent_node->specialization_key ); $var_node = DataFlowNode::getForAssignment( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php index 09677820309..dfb3d26ed14 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php @@ -175,14 +175,12 @@ public static function handleMagicMethod( /** * @return PhpParser\Node\Expr\ArrayItem */ - function (PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem { - return new VirtualArrayItem( - $arg->value, - null, - false, - $arg->getAttributes() - ); - }, + fn(PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem => new VirtualArrayItem( + $arg->value, + null, + false, + $arg->getAttributes() + ), $stmt->getArgs() ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php index 04f0b96cd6a..d37c9a2d4ff 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php @@ -225,9 +225,7 @@ public static function analyze( if (count($possible_new_class_types) > 0) { $class_type = array_reduce( $possible_new_class_types, - function (?Union $type_1, Union $type_2) use ($codebase): Union { - return Type::combineUnionTypes($type_1, $type_2, $codebase); - } + fn(?Union $type_1, Union $type_2): Union => Type::combineUnionTypes($type_1, $type_2, $codebase) ); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php index cb9143412dc..6882a4d9067 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php @@ -367,9 +367,7 @@ public static function handle( foreach ($anded_assertions as $assertions) { $referenced_var_ids = array_map( - function (array $_): bool { - return true; - }, + fn(array $_): bool => true, $assertions ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index 90ec3879495..034ff1a6d54 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -464,9 +464,7 @@ private static function analyzeNamedConstructor( $statements_analyzer->node_data->setIfTrueAssertions( $stmt, array_map( - function ($assertion) use ($generic_params, $codebase) { - return $assertion->getUntemplatedCopy($generic_params, null, $codebase); - }, + fn($assertion) => $assertion->getUntemplatedCopy($generic_params, null, $codebase), $method_storage->if_true_assertions ) ); @@ -476,9 +474,7 @@ function ($assertion) use ($generic_params, $codebase) { $statements_analyzer->node_data->setIfFalseAssertions( $stmt, array_map( - function ($assertion) use ($generic_params, $codebase) { - return $assertion->getUntemplatedCopy($generic_params, null, $codebase); - }, + fn($assertion) => $assertion->getUntemplatedCopy($generic_params, null, $codebase), $method_storage->if_false_assertions ) ); @@ -500,17 +496,13 @@ function ($assertion) use ($generic_params, $codebase) { $template_name, $storage->template_extended_params, array_map( - function ($type_map) use ($codebase) { - return array_map( - function ($bounds) use ($codebase) { - return TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds( - $bounds, - $codebase - ); - }, - $type_map - ); - }, + fn($type_map) => array_map( + fn($bounds) => TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds( + $bounds, + $codebase + ), + $type_map + ), $template_result->lower_bounds ) ); @@ -555,9 +547,7 @@ function ($bounds) use ($codebase) { $fq_class_name, array_values( array_map( - function ($map) { - return clone reset($map); - }, + fn($map) => clone reset($map), $storage->template_types ) ) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php index 800e30bfca3..fba791e191a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php @@ -402,9 +402,10 @@ private static function handleNamedCall( $mixin_candidates[] = clone $mixin_candidate; } - $mixin_candidates_no_generic = array_filter($mixin_candidates, function ($check): bool { - return !($check instanceof TGenericObject); - }); + $mixin_candidates_no_generic = array_filter( + $mixin_candidates, + fn($check): bool => !($check instanceof TGenericObject) + ); // $mixin_candidates_no_generic will only be empty when there are TGenericObject entries. // In that case, Union will be initialized with an empty array but @@ -615,14 +616,12 @@ private static function handleNamedCall( } $array_values = array_map( - function (PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem { - return new VirtualArrayItem( - $arg->value, - null, - false, - $arg->getAttributes() - ); - }, + fn(PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem => new VirtualArrayItem( + $arg->value, + null, + false, + $arg->getAttributes() + ), $args ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php index 719b9225ae7..8f1a6773eb6 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php @@ -330,9 +330,8 @@ public static function analyze( $statements_analyzer->node_data->setIfTrueAssertions( $stmt, array_map( - function (Assertion $assertion) use ($generic_params, $codebase): Assertion { - return $assertion->getUntemplatedCopy($generic_params, null, $codebase); - }, + fn(Assertion $assertion): Assertion => + $assertion->getUntemplatedCopy($generic_params, null, $codebase), $method_storage->if_true_assertions ) ); @@ -342,9 +341,8 @@ function (Assertion $assertion) use ($generic_params, $codebase): Assertion { $statements_analyzer->node_data->setIfFalseAssertions( $stmt, array_map( - function (Assertion $assertion) use ($generic_params, $codebase): Assertion { - return $assertion->getUntemplatedCopy($generic_params, null, $codebase); - }, + fn(Assertion $assertion): Assertion => + $assertion->getUntemplatedCopy($generic_params, null, $codebase), $method_storage->if_false_assertions ) ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index 851172f3aca..941d88f7088 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -892,17 +892,13 @@ public static function applyAssertionsToContext( if ($type_assertions) { $template_type_map = array_map( - function ($type_map) use ($codebase) { - return array_map( - function ($bounds) use ($codebase) { - return TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds( - $bounds, - $codebase - ); - }, - $type_map - ); - }, + fn($type_map) => array_map( + fn($bounds) => TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds( + $bounds, + $codebase + ), + $type_map + ), $inferred_lower_bounds ); @@ -1106,9 +1102,7 @@ public static function checkTemplateResult( if (count($lower_bounds) > 1) { $bounds_with_equality = array_filter( $lower_bounds, - function ($lower_bound) { - return (bool)$lower_bound->equality_bound_classlike; - } + fn($lower_bound) => (bool)$lower_bound->equality_bound_classlike ); if (!$bounds_with_equality) { @@ -1117,9 +1111,7 @@ function ($lower_bound) { $equality_types = array_unique( array_map( - function ($bound_with_equality) { - return $bound_with_equality->type->getId(); - }, + fn($bound_with_equality) => $bound_with_equality->type->getId(), $bounds_with_equality ) ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index 09a30c4262b..53a85d7a39a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -1575,9 +1575,7 @@ private static function handleArrayAccessOnKeyedArray( $formatted_keys = implode( ', ', array_map( - function ($key) { - return is_int($key) ? $key : '\'' . $key . '\''; - }, + fn($key) => is_int($key) ? $key : '\'' . $key . '\'', $object_like_keys ) ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php index d08f44fbccf..2e2ee100d94 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php @@ -264,12 +264,10 @@ public static function analyze( if (isset($vars_in_scope_reconciled[$switch_var_id])) { $array_literal_types = array_filter( $vars_in_scope_reconciled[$switch_var_id]->getAtomicTypes(), - function ($type) { - return $type instanceof TLiteralInt - || $type instanceof TLiteralString - || $type instanceof TLiteralFloat - || $type instanceof TEnumCase; - } + fn($type) => $type instanceof TLiteralInt + || $type instanceof TLiteralString + || $type instanceof TLiteralFloat + || $type instanceof TEnumCase ); if ($array_literal_types) { @@ -314,9 +312,8 @@ private static function convertCondsToConditional( } $array_items = array_map( - function ($cond): PhpParser\Node\Expr\ArrayItem { - return new VirtualArrayItem($cond, null, false, $cond->getAttributes()); - }, + fn($cond): PhpParser\Node\Expr\ArrayItem => + new VirtualArrayItem($cond, null, false, $cond->getAttributes()), $conds ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php index 872c7346e1c..d436ebcaae0 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php @@ -129,9 +129,7 @@ function (Clause $c) use ($mixed_var_ids, $cond_id): Clause { $ternary_clauses = array_values( array_filter( $ternary_clauses, - function ($c) use ($reconciled_expression_clauses): bool { - return !in_array($c->hash, $reconciled_expression_clauses); - } + fn($c): bool => !in_array($c->hash, $reconciled_expression_clauses) ) ); } diff --git a/src/Psalm/Internal/Cli/LanguageServer.php b/src/Psalm/Internal/Cli/LanguageServer.php index 7bc72068d1c..22fd434ed87 100644 --- a/src/Psalm/Internal/Cli/LanguageServer.php +++ b/src/Psalm/Internal/Cli/LanguageServer.php @@ -224,9 +224,8 @@ function (string $arg) use ($valid_long_options): void { $first_autoloader = $include_collector->runAndCollect( // we ignore the FQN because of a hack in scoper.inc that needs full path // phpcs:ignore SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName - function () use ($current_dir, $options, $vendor_dir): ?\Composer\Autoload\ClassLoader { - return CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir); - } + fn(): ?\Composer\Autoload\ClassLoader => + CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir) ); if (array_key_exists('v', $options)) { diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 7f46fafc157..b512574332b 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -217,9 +217,8 @@ public static function run(array $argv): void $first_autoloader = $include_collector->runAndCollect( // we ignore the FQN because of a hack in scoper.inc that needs full path // phpcs:ignore SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName - function () use ($current_dir, $options, $vendor_dir): ?\Composer\Autoload\ClassLoader { - return CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir); - } + fn(): ?\Composer\Autoload\ClassLoader => + CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir) ); $run_taint_analysis = self::shouldRunTaintAnalysis($options); @@ -501,18 +500,16 @@ private static function generateConfig(string $current_dir, array &$args): void $args = array_values(array_filter( $args, - function (string $arg): bool { - return $arg !== '--ansi' - && $arg !== '--no-ansi' - && $arg !== '-i' - && $arg !== '--init' - && $arg !== '--debug' - && $arg !== '--debug-by-line' - && $arg !== '--debug-emitted-issues' - && strpos($arg, '--disable-extension=') !== 0 - && strpos($arg, '--root=') !== 0 - && strpos($arg, '--r=') !== 0; - } + fn(string $arg): bool => $arg !== '--ansi' + && $arg !== '--no-ansi' + && $arg !== '-i' + && $arg !== '--init' + && $arg !== '--debug' + && $arg !== '--debug-by-line' + && $arg !== '--debug-emitted-issues' + && strpos($arg, '--disable-extension=') !== 0 + && strpos($arg, '--root=') !== 0 + && strpos($arg, '--r=') !== 0 )); $init_level = null; @@ -1073,9 +1070,7 @@ private static function storeFlowGraph(array $options, ProjectAnalyzer $project_ if ($flow_graph !== null && $dump_taint_graph !== null) { file_put_contents($dump_taint_graph, "digraph Taints {\n\t". implode("\n\t", array_map( - function (array $edges) { - return '"'.implode('" -> "', $edges).'"'; - }, + fn(array $edges) => '"'.implode('" -> "', $edges).'"', $flow_graph->summarizeEdges() )) . "\n}\n"); diff --git a/src/Psalm/Internal/Cli/Psalter.php b/src/Psalm/Internal/Cli/Psalter.php index 2975f0251ef..7d02744e738 100644 --- a/src/Psalm/Internal/Cli/Psalter.php +++ b/src/Psalm/Internal/Cli/Psalter.php @@ -207,9 +207,8 @@ public static function run(array $argv): void $first_autoloader = $include_collector->runAndCollect( // we ignore the FQN because of a hack in scoper.inc that needs full path // phpcs:ignore SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName - function () use ($current_dir, $options, $vendor_dir): ?\Composer\Autoload\ClassLoader { - return CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir); - } + fn(): ?\Composer\Autoload\ClassLoader => + CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir) ); diff --git a/src/Psalm/Internal/Cli/Refactor.php b/src/Psalm/Internal/Cli/Refactor.php index c4e90cdaf70..af1a59d12f8 100644 --- a/src/Psalm/Internal/Cli/Refactor.php +++ b/src/Psalm/Internal/Cli/Refactor.php @@ -182,9 +182,8 @@ function (string $arg) use ($valid_long_options): void { $first_autoloader = $include_collector->runAndCollect( // we ignore the FQN because of a hack in scoper.inc that needs full path // phpcs:ignore SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName - function () use ($current_dir, $options, $vendor_dir): ?\Composer\Autoload\ClassLoader { - return CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir); - } + fn(): ?\Composer\Autoload\ClassLoader => + CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir) ); // If Xdebug is enabled, restart without it diff --git a/src/Psalm/Internal/Codebase/Analyzer.php b/src/Psalm/Internal/Codebase/Analyzer.php index 36c927131d3..7c5c1ea9dbe 100644 --- a/src/Psalm/Internal/Codebase/Analyzer.php +++ b/src/Psalm/Internal/Codebase/Analyzer.php @@ -283,9 +283,7 @@ public function analyzeFiles( $this->files_to_analyze = array_filter( $this->files_to_analyze, - function (string $file_path): bool { - return $this->file_provider->fileExists($file_path); - } + fn(string $file_path): bool => $this->file_provider->fileExists($file_path) ); $this->doAnalysis($project_analyzer, $pool_size); diff --git a/src/Psalm/Internal/Codebase/ClassLikes.php b/src/Psalm/Internal/Codebase/ClassLikes.php index 12119c7e8b8..658d7e194a5 100644 --- a/src/Psalm/Internal/Codebase/ClassLikes.php +++ b/src/Psalm/Internal/Codebase/ClassLikes.php @@ -1604,29 +1604,23 @@ public function getConstantsForClass(string $class_name, int $visibility): array if ($visibility === ReflectionProperty::IS_PUBLIC) { return array_filter( $storage->constants, - function ($constant) { - return $constant->type - && $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC; - } + fn($constant) => $constant->type + && $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC ); } if ($visibility === ReflectionProperty::IS_PROTECTED) { return array_filter( $storage->constants, - function ($constant) { - return $constant->type - && ($constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC - || $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED); - } + fn($constant) => $constant->type + && ($constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC + || $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED) ); } return array_filter( $storage->constants, - function ($constant) { - return $constant->type !== null; - } + fn($constant) => $constant->type !== null ); } diff --git a/src/Psalm/Internal/Codebase/Populator.php b/src/Psalm/Internal/Codebase/Populator.php index 47270665257..15cc6fc8304 100644 --- a/src/Psalm/Internal/Codebase/Populator.php +++ b/src/Psalm/Internal/Codebase/Populator.php @@ -526,10 +526,8 @@ private function populateDataFromParentClass( $storage->constants = array_merge( array_filter( $parent_storage->constants, - function ($constant) { - return $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC - || $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED; - } + fn($constant) => $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC + || $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED ), $storage->constants ); @@ -587,9 +585,7 @@ private function populateInterfaceDataFromParentInterfaces( $storage->constants = array_merge( array_filter( $parent_interface_storage->constants, - function ($constant) { - return $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC; - } + fn($constant) => $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC ), $storage->constants ); @@ -693,9 +689,7 @@ private function populateDataFromImplementedInterfaces( $storage->constants = array_merge( array_filter( $implemented_interface_storage->constants, - function ($constant) { - return $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC; - } + fn($constant) => $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC ), $storage->constants ); diff --git a/src/Psalm/Internal/Codebase/Reflection.php b/src/Psalm/Internal/Codebase/Reflection.php index ed3b3fc1b2d..69348f04785 100644 --- a/src/Psalm/Internal/Codebase/Reflection.php +++ b/src/Psalm/Internal/Codebase/Reflection.php @@ -431,9 +431,7 @@ public static function getPsalmTypeFromReflectionType(?ReflectionType $reflectio $type = implode( '|', array_map( - function (ReflectionNamedType $reflection) { - return $reflection->getName(); - }, + fn(ReflectionNamedType $reflection) => $reflection->getName(), $reflection_type->getTypes() ) ); diff --git a/src/Psalm/Internal/Codebase/Scanner.php b/src/Psalm/Internal/Codebase/Scanner.php index fb6e1bf5c63..e510bf63827 100644 --- a/src/Psalm/Internal/Codebase/Scanner.php +++ b/src/Psalm/Internal/Codebase/Scanner.php @@ -316,11 +316,9 @@ private function scanFilePaths(int $pool_size): bool $filetype_scanners = $this->config->getFiletypeScanners(); $files_to_scan = array_filter( $this->files_to_scan, - function (string $file_path): bool { - return $this->file_provider->fileExists($file_path) - && (!isset($this->scanned_files[$file_path]) - || (isset($this->files_to_deep_scan[$file_path]) && !$this->scanned_files[$file_path])); - } + fn(string $file_path): bool => $this->file_provider->fileExists($file_path) + && (!isset($this->scanned_files[$file_path]) + || (isset($this->files_to_deep_scan[$file_path]) && !$this->scanned_files[$file_path])) ); $this->files_to_scan = []; diff --git a/src/Psalm/Internal/Codebase/TaintFlowGraph.php b/src/Psalm/Internal/Codebase/TaintFlowGraph.php index f493bfab21b..1f61889f6b4 100644 --- a/src/Psalm/Internal/Codebase/TaintFlowGraph.php +++ b/src/Psalm/Internal/Codebase/TaintFlowGraph.php @@ -521,9 +521,7 @@ private function getSpecializedSources(DataFlowNode $source): array return array_filter( $generated_sources, - function ($new_source): bool { - return isset($this->forward_edges[$new_source->id]); - } + fn($new_source): bool => isset($this->forward_edges[$new_source->id]) ); } } diff --git a/src/Psalm/Internal/Fork/PsalmRestarter.php b/src/Psalm/Internal/Fork/PsalmRestarter.php index 0ca2e1ac9d4..4f55442b336 100644 --- a/src/Psalm/Internal/Fork/PsalmRestarter.php +++ b/src/Psalm/Internal/Fork/PsalmRestarter.php @@ -39,9 +39,7 @@ protected function requiresRestart($default): bool { $this->required = (bool) array_filter( $this->disabledExtensions, - function (string $extension): bool { - return extension_loaded($extension); - } + fn(string $extension): bool => extension_loaded($extension) ); return $default || $this->required; diff --git a/src/Psalm/Internal/PhpVisitor/CloningVisitor.php b/src/Psalm/Internal/PhpVisitor/CloningVisitor.php index 39a03245db1..5ea8d9430ec 100644 --- a/src/Psalm/Internal/PhpVisitor/CloningVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/CloningVisitor.php @@ -29,9 +29,7 @@ public function enterNode(Node $node): Node /** * @return Comment */ - function (Comment $c): Comment { - return clone $c; - }, + fn(Comment $c): Comment => clone $c, $cs ) ); diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php index 9e7fa21ebc5..9dffdfe8026 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php @@ -429,9 +429,7 @@ public function start(PhpParser\Node\Stmt\ClassLike $node): ?bool usort( $docblock_info->templates, - function (array $l, array $r): int { - return $l[4] > $r[4] ? 1 : -1; - } + fn(array $l, array $r): int => $l[4] > $r[4] ? 1 : -1 ); foreach ($docblock_info->templates as $i => $template_map) { diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php index 9eee2bf5ecb..d7dad776000 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php @@ -277,9 +277,7 @@ public static function addDocblockInfo( if ($storage instanceof MethodStorage) { $storage->has_docblock_param_types = (bool) array_filter( $storage->params, - function (FunctionLikeParameter $p): bool { - return $p->type !== null && $p->has_docblock_type; - } + fn(FunctionLikeParameter $p): bool => $p->type !== null && $p->has_docblock_type ); } @@ -866,9 +864,7 @@ private static function improveParamsFromDocblock( $params_without_docblock_type = array_filter( $storage->params, - function (FunctionLikeParameter $p): bool { - return !$p->has_docblock_type && (!$p->type || $p->type->hasArray()); - } + fn(FunctionLikeParameter $p): bool => !$p->has_docblock_type && (!$p->type || $p->type->hasArray()) ); if ($params_without_docblock_type) { diff --git a/src/Psalm/Internal/PluginManager/Command/ShowCommand.php b/src/Psalm/Internal/PluginManager/Command/ShowCommand.php index 50fc46492fe..ce72f1cda02 100644 --- a/src/Psalm/Internal/PluginManager/Command/ShowCommand.php +++ b/src/Psalm/Internal/PluginManager/Command/ShowCommand.php @@ -59,9 +59,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * @return array{0: null|string, 1: string} */ - function (string $class, ?string $package): array { - return [$package, $class]; - }; + fn(string $class, ?string $package): array => [$package, $class]; $io->section('Enabled'); if (count($enabled)) { diff --git a/src/Psalm/Internal/Provider/FileReferenceProvider.php b/src/Psalm/Internal/Provider/FileReferenceProvider.php index 9419bfdcab4..ece9a2ac06e 100644 --- a/src/Psalm/Internal/Provider/FileReferenceProvider.php +++ b/src/Psalm/Internal/Provider/FileReferenceProvider.php @@ -186,9 +186,7 @@ public function getDeletedReferencedFiles(): array if (self::$deleted_files === null) { self::$deleted_files = array_filter( array_keys(self::$file_references), - function (string $file_name): bool { - return !file_exists($file_name); - } + fn(string $file_name): bool => !file_exists($file_name) ); } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php index d0ef928c440..45d2955efde 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php @@ -111,9 +111,7 @@ static function ($keyed_type) use ($statements_source, $context) { }, $first_arg_array->properties ), - static function ($keyed_type) { - return !$keyed_type->isNever(); - } + static fn($keyed_type) => !$keyed_type->isNever() ); if (!$new_properties) { diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php index 2d64e4b8757..882acf8b6a0 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php @@ -197,9 +197,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev /** * @return Union */ - function (Union $_) use ($mapping_return_type): Union { - return clone $mapping_return_type; - }, + fn(Union $_): Union => clone $mapping_return_type, $array_arg_atomic_type->properties ) ); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php index b6279c0d35b..d8eba498963 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php @@ -91,18 +91,14 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($event->getFunctionId() === 'min') { assert(count($min_bounds) !== 0); //null values in $max_bounds doesn't make sense for min() so we remove them - $max_bounds = array_filter($max_bounds, function ($v) { - return $v !== null; - }) ?: [null]; + $max_bounds = array_filter($max_bounds, fn($v) => $v !== null) ?: [null]; $min_potential_int = in_array(null, $min_bounds, true) ? null : min($min_bounds); $max_potential_int = in_array(null, $max_bounds, true) ? null : min($max_bounds); } else { assert(count($max_bounds) !== 0); //null values in $min_bounds doesn't make sense for max() so we remove them - $min_bounds = array_filter($min_bounds, function ($v) { - return $v !== null; - }) ?: [null]; + $min_bounds = array_filter($min_bounds, fn($v) => $v !== null) ?: [null]; $min_potential_int = in_array(null, $min_bounds, true) ? null : max($min_bounds); $max_potential_int = in_array(null, $max_bounds, true) ? null : max($max_bounds); diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index b157026799c..03df6b2f113 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -217,16 +217,12 @@ public function getStatementsForFile( ); $unchanged_members = array_map( - function (int $_): bool { - return true; - }, + fn(int $_): bool => true, array_flip($unchanged_members) ); $unchanged_signature_members = array_map( - function (int $_): bool { - return true; - }, + fn(int $_): bool => true, array_flip($unchanged_signature_members) ); @@ -249,9 +245,7 @@ function (string $key) use ($file_path_hash): string { * * @return bool */ - function ($_): bool { - return true; - }, + fn($_): bool => true, array_flip($changed_members) ); diff --git a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php index a090faee460..ce43e4cb664 100644 --- a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php @@ -192,9 +192,7 @@ public static function isContainedBy( // if the array has a known size < 10, make sure the array keys are literal ints if ($input_type_part->count !== null && $input_type_part->count < 10) { $literal_ints = array_map( - function ($i) { - return new TLiteralInt($i); - }, + fn($i) => new TLiteralInt($i), range(0, $input_type_part->count - 1) ); diff --git a/src/Psalm/Internal/Type/TemplateResult.php b/src/Psalm/Internal/Type/TemplateResult.php index c92e4af20c9..b4d2c17986e 100644 --- a/src/Psalm/Internal/Type/TemplateResult.php +++ b/src/Psalm/Internal/Type/TemplateResult.php @@ -61,14 +61,10 @@ public function __construct(array $template_types, array $lower_bounds) $this->template_types = $template_types; $this->lower_bounds = array_map( - function ($type_map) { - return array_map( - function ($type) { - return [new TemplateBound($type)]; - }, - $type_map - ); - }, + fn($type_map) => array_map( + fn($type) => [new TemplateBound($type)], + $type_map + ), $lower_bounds ); } diff --git a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php index 45cf799be45..939388c3429 100644 --- a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php +++ b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php @@ -1088,9 +1088,7 @@ public static function getMostSpecificTypeFromBounds(array $lower_bounds, ?Codeb usort( $lower_bounds, - function (TemplateBound $bound_a, TemplateBound $bound_b) { - return $bound_b->appearance_depth <=> $bound_a->appearance_depth; - } + fn(TemplateBound $bound_a, TemplateBound $bound_b) => $bound_b->appearance_depth <=> $bound_a->appearance_depth ); $current_depth = null; diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index 111a28561bd..78bcae708fc 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -1127,9 +1127,7 @@ private static function scrapeIntProperties( $all_nonnegative = !array_filter( $combination->ints, - function ($int): bool { - return $int->value < 0; - } + fn($int): bool => $int->value < 0 ); if (isset($combination->value_types['int'])) { @@ -1167,9 +1165,7 @@ function ($int): bool { if ($combination->ints) { $all_nonnegative = !array_filter( $combination->ints, - function ($int): bool { - return $int->value < 0; - } + fn($int): bool => $int->value < 0 ); if ($all_nonnegative) { @@ -1345,9 +1341,7 @@ private static function handleKeyedArrayEntries( ) { $combination->objectlike_entries = array_filter( $combination->objectlike_entries, - function (Union $type): bool { - return !$type->possibly_undefined; - } + fn(Union $type): bool => !$type->possibly_undefined ); } diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index 21875186c39..97c433680ee 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -255,10 +255,8 @@ public static function expandAtomic( if ($const_name_part) { $matching_constants = array_filter( $matching_constants, - function ($constant_name) use ($const_name_part): bool { - return $constant_name !== $const_name_part - && strpos($constant_name, $const_name_part) === 0; - } + fn($constant_name): bool => $constant_name !== $const_name_part + && strpos($constant_name, $const_name_part) === 0 ); } } else { @@ -609,18 +607,14 @@ private static function expandNamedObject( if ($container_class_storage->template_types && array_filter( $container_class_storage->template_types, - function ($type_map) { - return !reset($type_map)->hasMixed(); - } + fn($type_map) => !reset($type_map)->hasMixed() ) ) { $return_type = new TGenericObject( $return_type->value, array_values( array_map( - function ($type_map) { - return clone reset($type_map); - }, + fn($type_map) => clone reset($type_map), $container_class_storage->template_types ) ) @@ -830,9 +824,7 @@ private static function expandConditional( if ($number_of_types > 1) { $all_conditional_return_types = array_filter( $all_conditional_return_types, - static function (Atomic $atomic_type): bool { - return !$atomic_type instanceof TNever; - } + static fn(Atomic $atomic_type): bool => !$atomic_type instanceof TNever ); } @@ -841,9 +833,7 @@ static function (Atomic $atomic_type): bool { if ($number_of_types > 1) { $all_conditional_return_types = array_filter( $all_conditional_return_types, - static function (Atomic $atomic_type): bool { - return !$atomic_type instanceof TVoid; - } + static fn(Atomic $atomic_type): bool => !$atomic_type instanceof TVoid ); if (count($all_conditional_return_types) !== $number_of_types) { diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index 26e2658ca4b..9b8c570914f 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -496,9 +496,7 @@ public static function getComputedIntsFromMask(array $potential_ints): array $potential_values = array_unique($potential_values); return array_map( - function ($int) { - return new TLiteralInt($int); - }, + fn($int) => new TLiteralInt($int), array_values($potential_values) ); } diff --git a/src/Psalm/Plugin/Shepherd.php b/src/Psalm/Plugin/Shepherd.php index 93192df66b6..2cdd05f56ab 100644 --- a/src/Psalm/Plugin/Shepherd.php +++ b/src/Psalm/Plugin/Shepherd.php @@ -64,9 +64,7 @@ public static function afterAnalysis( if ($build_info) { $normalized_data = $issues === [] ? [] : array_filter( array_merge(...array_values($issues)), - static function (IssueData $i): bool { - return $i->severity === 'error'; - } + static fn(IssueData $i): bool => $i->severity === 'error' ); $data = [ diff --git a/src/Psalm/Report.php b/src/Psalm/Report.php index 6ad69c849fc..e8441107321 100644 --- a/src/Psalm/Report.php +++ b/src/Psalm/Report.php @@ -90,9 +90,7 @@ public function __construct( if (!$report_options->show_info) { $this->issues_data = array_filter( $issues_data, - function ($issue_data): bool { - return $issue_data->severity !== Config::REPORT_INFO; - } + fn($issue_data): bool => $issue_data->severity !== Config::REPORT_INFO ); } else { $this->issues_data = $issues_data; diff --git a/src/Psalm/Report/CodeClimateReport.php b/src/Psalm/Report/CodeClimateReport.php index cdd1458b720..831774ede54 100644 --- a/src/Psalm/Report/CodeClimateReport.php +++ b/src/Psalm/Report/CodeClimateReport.php @@ -27,38 +27,21 @@ public function create(): string $options = $this->pretty ? Json::PRETTY : Json::DEFAULT; $issues_data = array_map( - function (IssueData $issue): array { - /** - * map fields to new structure. - * Expected fields: - * - type - * - check_name - * - description* - * - content - * - categories[] - * - severity - * - fingerprint* - * - location.path* - * - location.lines.begin* - * - * Fields with * are the one used by Gitlab for Code Quality - */ - return [ - 'type' => 'issue', - 'check_name' => $issue->type, - 'description' => $issue->message, - 'categories' => [$issue->type], - 'severity' => $this->convertSeverity($issue->severity), - 'fingerprint' => $this->calculateFingerprint($issue), - 'location' => [ - 'path' => $issue->file_name, - 'lines' => [ - 'begin' => $issue->line_from, - 'end' => $issue->line_to, - ], + fn(IssueData $issue): array => [ + 'type' => 'issue', + 'check_name' => $issue->type, + 'description' => $issue->message, + 'categories' => [$issue->type], + 'severity' => $this->convertSeverity($issue->severity), + 'fingerprint' => $this->calculateFingerprint($issue), + 'location' => [ + 'path' => $issue->file_name, + 'lines' => [ + 'begin' => $issue->line_from, + 'end' => $issue->line_to, ], - ]; - }, + ], + ], $this->issues_data ); diff --git a/src/Psalm/Report/XmlReport.php b/src/Psalm/Report/XmlReport.php index eea9c0561ad..0c4cfe9b86f 100644 --- a/src/Psalm/Report/XmlReport.php +++ b/src/Psalm/Report/XmlReport.php @@ -24,18 +24,14 @@ function (IssueData $issue_data): array { if (null !== $issue_data['taint_trace']) { $issue_data['taint_trace'] = array_map( - function ($trace): array { - return (array) $trace; - }, + fn($trace): array => (array) $trace, $issue_data['taint_trace'] ); } if (null !== $issue_data['other_references']) { $issue_data['other_references'] = array_map( - function (DataFlowNodeData $reference): array { - return (array) $reference; - }, + fn(DataFlowNodeData $reference): array => (array) $reference, $issue_data['other_references'] ); } diff --git a/src/Psalm/Storage/Assertion.php b/src/Psalm/Storage/Assertion.php index 53f13b1205f..0cfa0255f22 100644 --- a/src/Psalm/Storage/Assertion.php +++ b/src/Psalm/Storage/Assertion.php @@ -87,9 +87,7 @@ function (array $rules) use ($inferred_lower_bounds, $codebase): array { return [implode( '', array_map( - function ($f) { - return $f[0]; - }, + fn($f) => $f[0], $rule_tokens ) )]; diff --git a/src/Psalm/Storage/FunctionLikeStorage.php b/src/Psalm/Storage/FunctionLikeStorage.php index e9928ce565a..c06a43dea24 100644 --- a/src/Psalm/Storage/FunctionLikeStorage.php +++ b/src/Psalm/Storage/FunctionLikeStorage.php @@ -243,15 +243,21 @@ public function getSignature(bool $allow_newlines): string { $newlines = $allow_newlines && !empty($this->params); - $symbol_text = 'function ' . $this->cased_name . '(' . ($newlines ? "\n" : '') . implode( - ',' . ($newlines ? "\n" : ' '), - array_map( - function (FunctionLikeParameter $param) use ($newlines): string { - return ($newlines ? ' ' : '') . ($param->type ?: 'mixed') . ' $' . $param->name; - }, - $this->params + $symbol_text = 'function ' . $this->cased_name . '(' + . ($newlines ? "\n" : '') + . implode( + ',' . ($newlines ? "\n" : ' '), + array_map( + fn(FunctionLikeParameter $param): string => + ($newlines ? ' ' : '') + . ($param->type ?: 'mixed') + . ' $' . $param->name, + $this->params + ) ) - ) . ($newlines ? "\n" : '') . ') : ' . ($this->return_type ?: 'mixed'); + . ($newlines ? "\n" : '') + . ') : ' + . ($this->return_type ?: 'mixed'); if (!$this instanceof MethodStorage) { return $symbol_text; diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 1655de15f12..37f293b8aa6 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -350,9 +350,7 @@ public function isNamedObjectType(): bool && ($this->as->hasNamedObjectType() || array_filter( $this->extra_types ?: [], - function ($extra_type) { - return $extra_type->isNamedObjectType(); - } + fn($extra_type) => $extra_type->isNamedObjectType() ) ) ); @@ -403,9 +401,7 @@ public function hasTraversableInterface(Codebase $codebase): bool $this->extra_types && array_filter( $this->extra_types, - function (Atomic $a) use ($codebase): bool { - return $a->hasTraversableInterface($codebase); - } + fn(Atomic $a): bool => $a->hasTraversableInterface($codebase) ) ) ); @@ -428,9 +424,7 @@ public function hasCountableInterface(Codebase $codebase): bool $this->extra_types && array_filter( $this->extra_types, - function (Atomic $a) use ($codebase): bool { - return $a->hasCountableInterface($codebase); - } + fn(Atomic $a): bool => $a->hasCountableInterface($codebase) ) ) ); @@ -469,9 +463,7 @@ public function hasArrayAccessInterface(Codebase $codebase): bool $this->extra_types && array_filter( $this->extra_types, - function (Atomic $a) use ($codebase): bool { - return $a->hasArrayAccessInterface($codebase); - } + fn(Atomic $a): bool => $a->hasArrayAccessInterface($codebase) ) ) ); diff --git a/src/Psalm/Type/Atomic/GenericTrait.php b/src/Psalm/Type/Atomic/GenericTrait.php index 15d8715334e..2d1ea1c1fd7 100644 --- a/src/Psalm/Type/Atomic/GenericTrait.php +++ b/src/Psalm/Type/Atomic/GenericTrait.php @@ -58,9 +58,7 @@ public function getId(bool $nested = false): string $extra_types = '&' . implode( '&', array_map( - function ($type) { - return $type->getId(true); - }, + fn($type) => $type->getId(true), $this->extra_types ) ); @@ -147,9 +145,8 @@ public function toNamespacedString( /** * @return string */ - function (Atomic $extra_type) use ($namespace, $aliased_classes, $this_class): string { - return $extra_type->toNamespacedString($namespace, $aliased_classes, $this_class, false); - }, + fn(Atomic $extra_type): string => + $extra_type->toNamespacedString($namespace, $aliased_classes, $this_class, false), $this->extra_types ) ); @@ -163,9 +160,8 @@ function (Atomic $extra_type) use ($namespace, $aliased_classes, $this_class): s /** * @return string */ - function (Union $type_param) use ($namespace, $aliased_classes, $this_class): string { - return $type_param->toNamespacedString($namespace, $aliased_classes, $this_class, false); - }, + fn(Union $type_param): string => + $type_param->toNamespacedString($namespace, $aliased_classes, $this_class, false), $type_params ) ) . diff --git a/src/Psalm/Type/Atomic/HasIntersectionTrait.php b/src/Psalm/Type/Atomic/HasIntersectionTrait.php index 41743192c91..368ebfef191 100644 --- a/src/Psalm/Type/Atomic/HasIntersectionTrait.php +++ b/src/Psalm/Type/Atomic/HasIntersectionTrait.php @@ -38,19 +38,12 @@ private function getNamespacedIntersectionTypes( * * @return string */ - function (Atomic $extra_type) use ( + fn(Atomic $extra_type): string => $extra_type->toNamespacedString( $namespace, $aliased_classes, $this_class, $use_phpdoc_format - ): string { - return $extra_type->toNamespacedString( - $namespace, - $aliased_classes, - $this_class, - $use_phpdoc_format - ); - }, + ), $this->extra_types ) ); diff --git a/src/Psalm/Type/Atomic/TNamedObject.php b/src/Psalm/Type/Atomic/TNamedObject.php index 2ad72a83233..b922108c47e 100644 --- a/src/Psalm/Type/Atomic/TNamedObject.php +++ b/src/Psalm/Type/Atomic/TNamedObject.php @@ -69,9 +69,7 @@ public function getId(bool $nested = false): string return $this->value . '&' . implode( '&', array_map( - function ($type) { - return $type->getId(true); - }, + fn($type) => $type->getId(true), $this->extra_types ) ); diff --git a/src/Psalm/Type/Atomic/TObjectWithProperties.php b/src/Psalm/Type/Atomic/TObjectWithProperties.php index 34f799b3f68..308a61d7401 100644 --- a/src/Psalm/Type/Atomic/TObjectWithProperties.php +++ b/src/Psalm/Type/Atomic/TObjectWithProperties.php @@ -62,9 +62,7 @@ public function __toString(): string /** * @param string|int $name */ - function ($name, Union $type): string { - return $name . ($type->possibly_undefined ? '?' : '') . ':' . $type; - }, + fn($name, Union $type): string => $name . ($type->possibly_undefined ? '?' : '') . ':' . $type, array_keys($this->properties), $this->properties ) @@ -73,9 +71,7 @@ function ($name, Union $type): string { $methods_string = implode( ', ', array_map( - function (string $name): string { - return $name . '()'; - }, + fn(string $name): string => $name . '()', array_keys($this->methods) ) ); @@ -100,9 +96,7 @@ public function getId(bool $nested = false): string /** * @param string|int $name */ - function ($name, Union $type): string { - return $name . ($type->possibly_undefined ? '?' : '') . ':' . $type->getId(); - }, + fn($name, Union $type): string => $name . ($type->possibly_undefined ? '?' : '') . ':' . $type->getId(), array_keys($this->properties), $this->properties ) @@ -111,9 +105,7 @@ function ($name, Union $type): string { $methods_string = implode( ', ', array_map( - function (string $name): string { - return $name . '()'; - }, + fn(string $name): string => $name . '()', array_keys($this->methods) ) ); @@ -145,22 +137,16 @@ public function toNamespacedString( /** * @param string|int $name */ - function ( - $name, - Union $type - ) use ( - $namespace, - $aliased_classes, - $this_class, - $use_phpdoc_format - ): string { - return $name . ($type->possibly_undefined ? '?' : '') . ':' . $type->toNamespacedString( + fn($name, Union $type): string => + $name . + ($type->possibly_undefined ? '?' : '') + . ':' + . $type->toNamespacedString( $namespace, $aliased_classes, $this_class, $use_phpdoc_format - ); - }, + ), array_keys($this->properties), $this->properties ) diff --git a/src/Psalm/Type/Atomic/TTemplateParam.php b/src/Psalm/Type/Atomic/TTemplateParam.php index 517955bd3f3..593a8ff3a6d 100644 --- a/src/Psalm/Type/Atomic/TTemplateParam.php +++ b/src/Psalm/Type/Atomic/TTemplateParam.php @@ -62,9 +62,7 @@ public function getId(bool $nested = false): string { if ($this->extra_types) { return '(' . $this->param_name . ':' . $this->defining_class . ' as ' . $this->as->getId() - . ')&' . implode('&', array_map(function ($type) { - return $type->getId(true); - }, $this->extra_types)); + . ')&' . implode('&', array_map(fn($type) => $type->getId(true), $this->extra_types)); } return ($nested ? '(' : '') . $this->param_name diff --git a/src/Psalm/Type/Atomic/TTypeAlias.php b/src/Psalm/Type/Atomic/TTypeAlias.php index 9341e2e4a97..c3ff90378f9 100644 --- a/src/Psalm/Type/Atomic/TTypeAlias.php +++ b/src/Psalm/Type/Atomic/TTypeAlias.php @@ -52,9 +52,7 @@ public function getId(bool $nested = false): string return $this->getKey() . '&' . implode( '&', array_map( - function ($type) { - return $type->getId(true); - }, + fn($type) => $type->getId(true), $this->extra_types ) ); diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 9d864da44c2..b55074416b1 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -596,9 +596,7 @@ public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool return !array_filter( $types, - function ($atomic_type) use ($analysis_php_version_id) { - return !$atomic_type->canBeFullyExpressedInPhp($analysis_php_version_id); - } + fn($atomic_type) => !$atomic_type->canBeFullyExpressedInPhp($analysis_php_version_id) ); } @@ -687,9 +685,7 @@ public function isTemplatedClassString(): bool && count( array_filter( $this->types, - function ($type): bool { - return $type instanceof TTemplateParamClass; - } + fn($type): bool => $type instanceof TTemplateParamClass ) ) === 1; } @@ -698,9 +694,7 @@ public function hasArrayAccessInterface(Codebase $codebase): bool { return (bool)array_filter( $this->types, - function ($type) use ($codebase) { - return $type->hasArrayAccessInterface($codebase); - } + fn($type) => $type->hasArrayAccessInterface($codebase) ); } @@ -716,9 +710,7 @@ public function getCallableTypes(): array { return array_filter( $this->types, - function ($type): bool { - return $type instanceof TCallable; - } + fn($type): bool => $type instanceof TCallable ); } @@ -729,9 +721,7 @@ public function getClosureTypes(): array { return array_filter( $this->types, - function ($type): bool { - return $type instanceof TClosure; - } + fn($type): bool => $type instanceof TClosure ); } @@ -861,9 +851,7 @@ public function hasLiteralClassString(): bool public function hasInt(): bool { return isset($this->types['int']) || isset($this->types['array-key']) || $this->literal_int_types - || array_filter($this->types, function (Atomic $type) { - return $type instanceof TIntRange; - }); + || array_filter($this->types, fn(Atomic $type) => $type instanceof TIntRange); } public function hasPositiveInt(): bool @@ -913,18 +901,14 @@ public function hasTemplate(): bool { return (bool) array_filter( $this->types, - function (Atomic $type): bool { - return $type instanceof TTemplateParam - || ($type instanceof TNamedObject - && $type->extra_types - && array_filter( - $type->extra_types, - function ($t): bool { - return $t instanceof TTemplateParam; - } - ) - ); - } + fn(Atomic $type): bool => $type instanceof TTemplateParam + || ($type instanceof TNamedObject + && $type->extra_types + && array_filter( + $type->extra_types, + fn($t): bool => $t instanceof TTemplateParam + ) + ) ); } @@ -932,9 +916,7 @@ public function hasConditional(): bool { return (bool) array_filter( $this->types, - function (Atomic $type): bool { - return $type instanceof TConditional; - } + fn(Atomic $type): bool => $type instanceof TConditional ); } @@ -942,21 +924,17 @@ public function hasTemplateOrStatic(): bool { return (bool) array_filter( $this->types, - function (Atomic $type): bool { - return $type instanceof TTemplateParam - || ($type instanceof TNamedObject - && ($type->was_static - || ($type->extra_types - && array_filter( - $type->extra_types, - function ($t): bool { - return $t instanceof TTemplateParam; - } - ) + fn(Atomic $type): bool => $type instanceof TTemplateParam + || ($type instanceof TNamedObject + && ($type->was_static + || ($type->extra_types + && array_filter( + $type->extra_types, + fn($t): bool => $t instanceof TTemplateParam ) ) - ); - } + ) + ) ); } @@ -1185,13 +1163,11 @@ public function isInt(bool $check_templates = false): bool return count( array_filter( $this->types, - function ($type) use ($check_templates): bool { - return $type instanceof TInt - || ($check_templates - && $type instanceof TTemplateParam - && $type->as->isInt() - ); - } + fn($type): bool => $type instanceof TInt + || ($check_templates + && $type instanceof TTemplateParam + && $type->as->isInt() + ) ) ) === count($this->types); } @@ -1216,13 +1192,11 @@ public function isString(bool $check_templates = false): bool return count( array_filter( $this->types, - function ($type) use ($check_templates): bool { - return $type instanceof TString - || ($check_templates - && $type instanceof TTemplateParam - && $type->as->isString() - ); - } + fn($type): bool => $type instanceof TString + || ($check_templates + && $type instanceof TTemplateParam + && $type->as->isString() + ) ) ) === count($this->types); } diff --git a/tests/CodebaseTest.php b/tests/CodebaseTest.php index 36dee856086..015d95af583 100644 --- a/tests/CodebaseTest.php +++ b/tests/CodebaseTest.php @@ -160,9 +160,7 @@ public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event) ? (string)$stmt->extends->getAttribute('resolvedName') : ''; $storage->custom_metadata['implements'] = array_map( - function (Name $aspect): string { - return (string)$aspect->getAttribute('resolvedName'); - }, + fn(Name $aspect): string => (string)$aspect->getAttribute('resolvedName'), $stmt->implements ); $storage->custom_metadata['a'] = 'b'; diff --git a/tests/Config/ConfigTest.php b/tests/Config/ConfigTest.php index 2ec4fcfac9d..9fd9205d03a 100644 --- a/tests/Config/ConfigTest.php +++ b/tests/Config/ConfigTest.php @@ -792,9 +792,7 @@ public function testAllPossibleIssues(): void * * @return string */ - function ($issue_name): string { - return '<' . $issue_name . ' errorLevel="suppress" />' . "\n"; - }, + fn($issue_name): string => '<' . $issue_name . ' errorLevel="suppress" />' . "\n", IssueHandler::getAllIssueTypes() ) ); diff --git a/tests/DocumentationTest.php b/tests/DocumentationTest.php index 61e9fdf5450..0cdd6fb75ca 100644 --- a/tests/DocumentationTest.php +++ b/tests/DocumentationTest.php @@ -347,9 +347,7 @@ public function testShortcodesAreUnique(): void $duplicate_shortcodes = array_filter( $all_shortcodes, - function ($issues): bool { - return count($issues) > 1; - } + fn($issues): bool => count($issues) > 1 ); $this->assertEquals( diff --git a/tests/FileDiffTest.php b/tests/FileDiffTest.php index 821e35b687f..3b18fdb6900 100644 --- a/tests/FileDiffTest.php +++ b/tests/FileDiffTest.php @@ -65,9 +65,7 @@ public function testCode( * * @return array{0: int, 1: int} */ - function (array $arr): array { - return [$arr[2], $arr[3]]; - }, + fn(array $arr): array => [$arr[2], $arr[3]], $diff[3] ); @@ -141,9 +139,7 @@ public function testPartialAstDiff( * * @return array{0: int, 1: int} */ - function (array $arr): array { - return [$arr[2], $arr[3]]; - }, + fn(array $arr): array => [$arr[2], $arr[3]], $diff[3] ); diff --git a/tests/TaintTest.php b/tests/TaintTest.php index cfae0743390..b4859519899 100644 --- a/tests/TaintTest.php +++ b/tests/TaintTest.php @@ -2309,9 +2309,7 @@ public function multipleTaintIssuesAreDetected(string $code, array $expectedIssu $this->analyzeFile($filePath, new Context(), false); $actualIssueTypes = array_map( - function (IssueData $issue): string { - return $issue->type . '{ ' . trim($issue->snippet) . ' }'; - }, + fn(IssueData $issue): string => $issue->type . '{ ' . trim($issue->snippet) . ' }', IssueBuffer::getIssuesDataForFile($filePath) ); self::assertSame($expectedIssuesTypes, $actualIssueTypes); diff --git a/tests/TestCase.php b/tests/TestCase.php index f48b50cc9c4..84a50bfff6b 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -183,9 +183,7 @@ public static function assertArrayKeysAreStrings(array $array, string $message = public static function assertArrayKeysAreZeroOrString(array $array, string $message = ''): void { - $isZeroOrString = /** @param mixed $key */ function ($key): bool { - return $key === 0 || is_string($key); - }; + $isZeroOrString = /** @param mixed $key */ fn($key): bool => $key === 0 || is_string($key); $validKeys = array_filter($array, $isZeroOrString, ARRAY_FILTER_USE_KEY); self::assertTrue(count($array) === count($validKeys), $message); } diff --git a/tests/TypeComparatorTest.php b/tests/TypeComparatorTest.php index 4496e8fa94f..b051d8c60ec 100644 --- a/tests/TypeComparatorTest.php +++ b/tests/TypeComparatorTest.php @@ -77,9 +77,7 @@ public function getAllBasicTypes(): array ] ); return array_map( - function ($type) { - return [$type]; - }, + fn($type) => [$type], array_keys($basic_types) ); } From 638a10dd39f8e44d2d62aabe19461ba2323d0475 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Thu, 6 Jan 2022 01:58:52 +0200 Subject: [PATCH 71/78] Restore return docblock --- src/Psalm/Report/CodeClimateReport.php | 42 ++++++++++++++++++-------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/Psalm/Report/CodeClimateReport.php b/src/Psalm/Report/CodeClimateReport.php index 831774ede54..27523ab3627 100644 --- a/src/Psalm/Report/CodeClimateReport.php +++ b/src/Psalm/Report/CodeClimateReport.php @@ -27,21 +27,37 @@ public function create(): string $options = $this->pretty ? Json::PRETTY : Json::DEFAULT; $issues_data = array_map( - fn(IssueData $issue): array => [ - 'type' => 'issue', - 'check_name' => $issue->type, - 'description' => $issue->message, - 'categories' => [$issue->type], - 'severity' => $this->convertSeverity($issue->severity), - 'fingerprint' => $this->calculateFingerprint($issue), - 'location' => [ - 'path' => $issue->file_name, - 'lines' => [ - 'begin' => $issue->line_from, - 'end' => $issue->line_to, + fn(IssueData $issue): array => + /** + * map fields to new structure. + * Expected fields: + * - type + * - check_name + * - description* + * - content + * - categories[] + * - severity + * - fingerprint* + * - location.path* + * - location.lines.begin* + * + * Fields with * are the one used by Gitlab for Code Quality + */ + [ + 'type' => 'issue', + 'check_name' => $issue->type, + 'description' => $issue->message, + 'categories' => [$issue->type], + 'severity' => $this->convertSeverity($issue->severity), + 'fingerprint' => $this->calculateFingerprint($issue), + 'location' => [ + 'path' => $issue->file_name, + 'lines' => [ + 'begin' => $issue->line_from, + 'end' => $issue->line_to, + ], ], ], - ], $this->issues_data ); From 4abbd9cb1bab330e9b5aa52ccdbd9d456b5a682e Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Fri, 7 Jan 2022 18:50:13 -0500 Subject: [PATCH 72/78] Simplify object comparison --- .../Type/Comparator/ObjectComparator.php | 460 +++++++++--------- src/Psalm/Type.php | 16 + tests/Template/FunctionTemplateTest.php | 4 +- 3 files changed, 253 insertions(+), 227 deletions(-) diff --git a/src/Psalm/Internal/Type/Comparator/ObjectComparator.php b/src/Psalm/Internal/Type/Comparator/ObjectComparator.php index b7b4ff301db..43facc0cf49 100644 --- a/src/Psalm/Internal/Type/Comparator/ObjectComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ObjectComparator.php @@ -7,12 +7,10 @@ use Psalm\Type\Atomic; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TNamedObject; -use Psalm\Type\Atomic\TNull; -use Psalm\Type\Atomic\TObject; use Psalm\Type\Atomic\TObjectWithProperties; use Psalm\Type\Atomic\TTemplateParam; +use Psalm\Type\Union; -use function array_merge; use function in_array; use function strpos; use function strtolower; @@ -34,35 +32,10 @@ public static function isShallowlyContainedBy( bool $allow_interface_equality, ?TypeComparisonResult $atomic_comparison_result ): bool { - $intersection_input_types = $input_type_part->extra_types ?: []; - $intersection_input_types[$input_type_part->getKey(false)] = $input_type_part; - - if ($input_type_part instanceof TTemplateParam) { - foreach ($input_type_part->as->getAtomicTypes() as $g) { - if ($g instanceof TNamedObject && $g->extra_types) { - $intersection_input_types = array_merge( - $intersection_input_types, - $g->extra_types - ); - } - } - } - - $intersection_container_types = $container_type_part->extra_types ?: []; - $intersection_container_types[$container_type_part->getKey(false)] = $container_type_part; - - if ($container_type_part instanceof TTemplateParam) { - foreach ($container_type_part->as->getAtomicTypes() as $g) { - if ($g instanceof TNamedObject && $g->extra_types) { - $intersection_container_types = array_merge( - $intersection_container_types, - $g->extra_types - ); - } - } - } + $intersection_input_types = self::getIntersectionTypes($input_type_part); + $intersection_container_types = self::getIntersectionTypes($container_type_part); - foreach ($intersection_container_types as $container_type_key => $intersection_container_type) { + foreach ($intersection_container_types as $intersection_container_type) { $container_was_static = false; if ($intersection_container_type instanceof TIterable) { @@ -70,73 +43,7 @@ public static function isShallowlyContainedBy( } elseif ($intersection_container_type instanceof TObjectWithProperties) { $intersection_container_type_lower = 'object'; } elseif ($intersection_container_type instanceof TTemplateParam) { - if (!$allow_interface_equality) { - if (isset($intersection_input_types[$container_type_key])) { - continue; - } - - foreach ($intersection_input_types as $intersection_input_type) { - if ($intersection_input_type instanceof TTemplateParam - && (strpos($intersection_container_type->defining_class, 'fn-') === 0 - || strpos($intersection_input_type->defining_class, 'fn-') === 0) - ) { - if (strpos($intersection_input_type->defining_class, 'fn-') === 0 - && strpos($intersection_container_type->defining_class, 'fn-') === 0 - && $intersection_input_type->defining_class - !== $intersection_container_type->defining_class - ) { - continue 2; - } - - foreach ($intersection_input_type->as->getAtomicTypes() as $input_as_atomic) { - if ($input_as_atomic->equals($intersection_container_type, false)) { - continue 3; - } - } - } elseif ($intersection_input_type instanceof TTemplateParam) { - $container_param = $intersection_container_type->param_name; - $container_class = $intersection_container_type->defining_class; - $input_class_like = $codebase->classlikes - ->getStorageFor($intersection_input_type->defining_class); - - if ($codebase->classlikes->traitExists($container_class) - && $input_class_like !== null - && isset( - $input_class_like->template_extended_params[$container_class][$container_param] - )) { - continue 2; - } - } - } - - return false; - } - - if ($intersection_container_type->as->isMixed()) { - continue; - } - $intersection_container_type_lower = null; - - foreach ($intersection_container_type->as->getAtomicTypes() as $g) { - if ($g instanceof TNull) { - continue; - } - - if ($g instanceof TObject) { - continue 2; - } - - if (!$g instanceof TNamedObject) { - continue 2; - } - - $intersection_container_type_lower = strtolower($g->value); - } - - if ($intersection_container_type_lower === null) { - return false; - } } else { $container_was_static = $intersection_container_type->was_static; @@ -147,165 +54,268 @@ public static function isShallowlyContainedBy( ); } - foreach ($intersection_input_types as $intersection_input_key => $intersection_input_type) { - $input_was_static = false; + $any_inputs_contained = false; - if ($intersection_input_type instanceof TIterable) { - $intersection_input_type_lower = 'iterable'; - } elseif ($intersection_input_type instanceof TObjectWithProperties) { - $intersection_input_type_lower = 'object'; - } elseif ($intersection_input_type instanceof TTemplateParam) { - if ($intersection_input_type->as->isMixed()) { - continue; - } - - $intersection_input_type_lower = null; + $container_type_is_interface = $intersection_container_type_lower + && $codebase->interfaceExists($intersection_container_type_lower); - foreach ($intersection_input_type->as->getAtomicTypes() as $g) { - if ($g instanceof TNull) { - continue; - } + foreach ($intersection_input_types as $input_type_key => $intersection_input_type) { + if ($allow_interface_equality + && $container_type_is_interface + && !isset($intersection_container_types[$input_type_key]) + ) { + $any_inputs_contained = true; + } elseif (self::isIntersectionShallowlyContainedBy( + $codebase, + $intersection_input_type, + $intersection_container_type, + $intersection_container_type_lower, + $container_was_static, + $allow_interface_equality, + $atomic_comparison_result + )) { + $any_inputs_contained = true; + } + } - if (!$g instanceof TNamedObject) { - continue 2; - } + if (!$any_inputs_contained) { + return false; + } + } - $intersection_input_type_lower = strtolower($g->value); - } + return true; + } - if ($intersection_input_type_lower === null) { - return false; + /** + * @param TNamedObject|TTemplateParam|TIterable $type_part + * @return array + */ + private static function getIntersectionTypes(Atomic $type_part): array + { + if (!$type_part->extra_types) { + if ($type_part instanceof TTemplateParam) { + $intersection_types = []; + + foreach ($type_part->as->getAtomicTypes() as $as_atomic_type) { + // T1 as T2 as object becomes (T1 as object) & (T2 as object) + if ($as_atomic_type instanceof TTemplateParam) { + $intersection_types += self::getIntersectionTypes($as_atomic_type); + $type_part = clone $type_part; + $type_part->as = $as_atomic_type->as; + $intersection_types[$type_part->getKey()] = $type_part; + + return $intersection_types; } - } else { - $input_was_static = $intersection_input_type->was_static; - - $intersection_input_type_lower = strtolower( - $codebase->classlikes->getUnAliasedName( - $intersection_input_type->value - ) - ); } + } - if ($intersection_container_type instanceof TTemplateParam - && $intersection_input_type instanceof TTemplateParam + return [$type_part->getKey() => $type_part]; + } + + $type_part = clone $type_part; + + $extra_types = $type_part->extra_types; + $type_part->extra_types = null; + + $extra_types[$type_part->getKey()] = $type_part; + + return $extra_types; + } + + /** + * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties $intersection_input_type + * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties $intersection_container_type + * + */ + private static function isIntersectionShallowlyContainedBy( + Codebase $codebase, + Atomic $intersection_input_type, + Atomic $intersection_container_type, + ?string $intersection_container_type_lower, + bool $container_was_static, + bool $allow_interface_equality, + ?TypeComparisonResult $atomic_comparison_result + ): bool { + if ($intersection_container_type instanceof TTemplateParam + && $intersection_input_type instanceof TTemplateParam + ) { + if (!$allow_interface_equality) { + if (strpos($intersection_container_type->defining_class, 'fn-') === 0 + || strpos($intersection_input_type->defining_class, 'fn-') === 0 ) { - if ($intersection_container_type->param_name !== $intersection_input_type->param_name - || ($intersection_container_type->defining_class - !== $intersection_input_type->defining_class - && strpos($intersection_input_type->defining_class, 'fn-') !== 0 - && strpos($intersection_container_type->defining_class, 'fn-') !== 0) + if (strpos($intersection_input_type->defining_class, 'fn-') === 0 + && strpos($intersection_container_type->defining_class, 'fn-') === 0 + && $intersection_input_type->defining_class + !== $intersection_container_type->defining_class ) { - if (strpos($intersection_input_type->defining_class, 'fn-') !== 0) { - $input_class_storage = $codebase->classlike_storage_provider->get( - $intersection_input_type->defining_class - ); - - if (isset($input_class_storage->template_extended_params - [$intersection_container_type->defining_class] - [$intersection_container_type->param_name]) - ) { - continue; - } - } + return true; + } - return false; + foreach ($intersection_input_type->as->getAtomicTypes() as $input_as_atomic) { + if ($input_as_atomic->equals($intersection_container_type, false)) { + return true; + } } } + } - if (!$intersection_container_type instanceof TTemplateParam - || $intersection_input_type instanceof TTemplateParam + if ($intersection_container_type->param_name === $intersection_input_type->param_name + && $intersection_container_type->defining_class === $intersection_input_type->defining_class + ) { + return true; + } + + if ($intersection_container_type->param_name !== $intersection_input_type->param_name + || ($intersection_container_type->defining_class + !== $intersection_input_type->defining_class + && strpos($intersection_input_type->defining_class, 'fn-') !== 0 + && strpos($intersection_container_type->defining_class, 'fn-') !== 0) + ) { + if (strpos($intersection_input_type->defining_class, 'fn-') === 0 + || strpos($intersection_container_type->defining_class, 'fn-') === 0 ) { - if ($intersection_container_type_lower === $intersection_input_type_lower) { - if ($container_was_static - && !$input_was_static - && !$intersection_input_type instanceof TTemplateParam - ) { - if ($atomic_comparison_result) { - $atomic_comparison_result->type_coerced = true; - } - - continue; - } + return false; + } - continue 2; - } + $input_class_storage = $codebase->classlike_storage_provider->get( + $intersection_input_type->defining_class + ); - if ($intersection_input_type_lower === 'generator' - && in_array($intersection_container_type_lower, ['iterator', 'traversable', 'iterable'], true) - ) { - continue 2; - } + if (isset($input_class_storage->template_extended_params + [$intersection_container_type->defining_class] + [$intersection_container_type->param_name]) + ) { + return true; + } + } - if ($intersection_container_type_lower === 'iterable') { - if ($intersection_input_type_lower === 'traversable' - || ($codebase->classlikes->classExists($intersection_input_type_lower) - && $codebase->classlikes->classImplements( - $intersection_input_type_lower, - 'Traversable' - )) - || ($codebase->classlikes->interfaceExists($intersection_input_type_lower) - && $codebase->classlikes->interfaceExtends( - $intersection_input_type_lower, - 'Traversable' - )) - ) { - continue 2; - } - } + return false; + } - if ($intersection_input_type_lower === 'traversable' - && $intersection_container_type_lower === 'iterable' - ) { - continue 2; - } + if ($intersection_container_type instanceof TTemplateParam + || $intersection_container_type_lower === null + ) { + return false; + } - $input_type_is_interface = $codebase->interfaceExists($intersection_input_type_lower); - $container_type_is_interface = $codebase->interfaceExists($intersection_container_type_lower); + if ($intersection_input_type instanceof TTemplateParam) { + $intersection_container_type = clone $intersection_container_type; - if ($allow_interface_equality - && $container_type_is_interface - && ($input_type_is_interface || !isset($intersection_container_types[$intersection_input_key])) - ) { - continue 2; - } + if ($intersection_container_type instanceof TNamedObject) { + // this is extra check is redundant since we're comparing to a template as type + $intersection_container_type->was_static = false; + } - if (($codebase->classExists($intersection_input_type_lower) - || $codebase->classlikes->enumExists($intersection_input_type_lower)) - && $codebase->classOrInterfaceExists($intersection_container_type_lower) - && $codebase->classExtendsOrImplements( - $intersection_input_type_lower, - $intersection_container_type_lower - ) - ) { - if ($container_was_static && !$input_was_static) { - if ($atomic_comparison_result) { - $atomic_comparison_result->type_coerced = true; - } + return UnionTypeComparator::isContainedBy( + $codebase, + $intersection_input_type->as, + new Union([$intersection_container_type]), + false, + false, + $atomic_comparison_result, + $allow_interface_equality + ); + } - continue; - } + $input_was_static = false; - continue 2; - } + if ($intersection_input_type instanceof TIterable) { + $intersection_input_type_lower = 'iterable'; + } elseif ($intersection_input_type instanceof TObjectWithProperties) { + $intersection_input_type_lower = 'object'; + } else { + $input_was_static = $intersection_input_type->was_static; - if ($input_type_is_interface - && $codebase->interfaceExtends( - $intersection_input_type_lower, - $intersection_container_type_lower - ) - ) { - continue 2; - } + $intersection_input_type_lower = strtolower( + $codebase->classlikes->getUnAliasedName( + $intersection_input_type->value + ) + ); + } + + if ($intersection_container_type_lower === $intersection_input_type_lower) { + if ($container_was_static && !$input_was_static) { + if ($atomic_comparison_result) { + $atomic_comparison_result->type_coerced = true; } - if (ExpressionAnalyzer::isMock($intersection_input_type_lower)) { - return true; + return false; + } + + return true; + } + + if ($intersection_input_type_lower === 'generator' + && in_array($intersection_container_type_lower, ['iterator', 'traversable', 'iterable'], true) + ) { + return true; + } + + if ($intersection_container_type_lower === 'iterable') { + if ($intersection_input_type_lower === 'traversable' + || ($codebase->classlikes->classExists($intersection_input_type_lower) + && $codebase->classlikes->classImplements( + $intersection_input_type_lower, + 'Traversable' + )) + || ($codebase->classlikes->interfaceExists($intersection_input_type_lower) + && $codebase->classlikes->interfaceExtends( + $intersection_input_type_lower, + 'Traversable' + )) + ) { + return true; + } + } + + if ($intersection_input_type_lower === 'traversable' + && $intersection_container_type_lower === 'iterable' + ) { + return true; + } + + $input_type_is_interface = $codebase->interfaceExists($intersection_input_type_lower); + $container_type_is_interface = $codebase->interfaceExists($intersection_container_type_lower); + + if ($allow_interface_equality + && $container_type_is_interface + && $input_type_is_interface + ) { + return true; + } + + if (($codebase->classExists($intersection_input_type_lower) + || $codebase->classlikes->enumExists($intersection_input_type_lower)) + && $codebase->classOrInterfaceExists($intersection_container_type_lower) + && $codebase->classExtendsOrImplements( + $intersection_input_type_lower, + $intersection_container_type_lower + ) + ) { + if ($container_was_static && !$input_was_static) { + if ($atomic_comparison_result) { + $atomic_comparison_result->type_coerced = true; } + + return false; } - return false; + return true; } - return true; + if ($input_type_is_interface + && $codebase->interfaceExtends( + $intersection_input_type_lower, + $intersection_container_type_lower + ) + ) { + return true; + } + + if (ExpressionAnalyzer::isMock($intersection_input_type_lower)) { + return true; + } + + return false; } } diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index 1d31caaac0a..8ba33ed81d5 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -704,6 +704,13 @@ private static function intersectAtomicTypes( $wider_type = $type_2_atomic; $intersection_performed = true; } + + if ($intersection_atomic + && !self::hasIntersection($type_1_atomic) + && !self::hasIntersection($type_2_atomic) + ) { + return $intersection_atomic; + } } if (static::mayHaveIntersection($type_1_atomic) @@ -763,4 +770,13 @@ private static function mayHaveIntersection(Atomic $type): bool || $type instanceof TTemplateParam || $type instanceof TObjectWithProperties; } + + private static function hasIntersection(Atomic $type): bool + { + return ($type instanceof TIterable + || $type instanceof TNamedObject + || $type instanceof TTemplateParam + || $type instanceof TObjectWithProperties + ) && $type->extra_types; + } } diff --git a/tests/Template/FunctionTemplateTest.php b/tests/Template/FunctionTemplateTest.php index f831dfc85b5..b2db707fcc9 100644 --- a/tests/Template/FunctionTemplateTest.php +++ b/tests/Template/FunctionTemplateTest.php @@ -2070,7 +2070,7 @@ interface Baz {} function returnsTemplatedIntersection(object $t) { return $t; }', - 'error_message' => 'InvalidReturnStatement', + 'error_message' => 'LessSpecificReturnStatement', ], 'returnIntersectionWhenTemplateIsExpectedBackward' => [ ' 'InvalidReturnStatement', + 'error_message' => 'LessSpecificReturnStatement', ], 'bottomTypeInClosureShouldClash' => [ ' Date: Sun, 9 Jan 2022 14:43:07 -0500 Subject: [PATCH 73/78] Fix empty class --- src/Psalm/Type/Reconciler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index 57225607f35..069484d39f9 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -233,7 +233,7 @@ public static function reconcileKeyedTypes( ); if ($result_type_candidate->isUnionEmpty()) { - $result_type_candidate->addType(new TEmpty); + $result_type_candidate->addType(new TNever); } $orred_type = Type::combineUnionTypes( From a435bc57b986258cb2e3f52d6c76fa8b470997bf Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sun, 9 Jan 2022 14:45:07 -0500 Subject: [PATCH 74/78] Rename was_static property to is_static to be more accurate --- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 4 ++-- src/Psalm/Internal/Analyzer/ClosureAnalyzer.php | 2 +- .../Internal/Analyzer/FunctionLikeAnalyzer.php | 2 +- .../Statements/Expression/Call/NewAnalyzer.php | 4 ++-- .../Expression/Fetch/ClassConstFetchAnalyzer.php | 2 +- src/Psalm/Internal/Type/AssertionReconciler.php | 2 +- .../Type/Comparator/AtomicTypeComparator.php | 8 ++++---- .../Type/Comparator/GenericTypeComparator.php | 4 ++-- .../Internal/Type/Comparator/ObjectComparator.php | 6 +++--- src/Psalm/Internal/Type/TypeCombiner.php | 6 +++--- src/Psalm/Internal/Type/TypeExpander.php | 8 ++++---- src/Psalm/Internal/Type/TypeParser.php | 2 +- src/Psalm/Type.php | 4 ++-- src/Psalm/Type/Atomic/GenericTrait.php | 2 +- src/Psalm/Type/Atomic/TAnonymousClassInstance.php | 4 ++-- src/Psalm/Type/Atomic/TNamedObject.php | 14 +++++++------- src/Psalm/Type/Union.php | 10 +++++----- 17 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index fe237e676f8..e4b95bed867 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -298,7 +298,7 @@ public function analyze( $union = new Union($mixins); $static_self = new TNamedObject($storage->name); - $static_self->was_static = true; + $static_self->is_static = true; $union = TypeExpander::expandUnion( $codebase, @@ -1227,7 +1227,7 @@ function (FunctionLikeParameter $param): PhpParser\Node\Arg { $method_context->self = $fq_class_name; $this_atomic_object_type = new TNamedObject($fq_class_name); - $this_atomic_object_type->was_static = !$storage->final; + $this_atomic_object_type->is_static = !$storage->final; $method_context->vars_in_scope['$this'] = new Union([$this_atomic_object_type]); $method_context->vars_possibly_in_scope['$this'] = true; diff --git a/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php b/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php index 1b36c9becf3..1f3d50de1a6 100644 --- a/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php @@ -95,7 +95,7 @@ public static function analyzeExpression( $use_context->vars_in_scope['$this'] = clone $context->vars_in_scope['$this']; } elseif ($context->self) { $this_atomic = new TNamedObject($context->self); - $this_atomic->was_static = true; + $this_atomic->is_static = true; $use_context->vars_in_scope['$this'] = new Union([$this_atomic]); } diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index f371bab0ec3..47e70f364d1 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -1811,7 +1811,7 @@ private function getFunctionInformation( $this_object_type = new TNamedObject($context->self); } - $this_object_type->was_static = !$storage->final; + $this_object_type->is_static = !$storage->final; if ($this->storage instanceof MethodStorage && $this->storage->if_this_is_type) { $template_result = new TemplateResult($this->getTemplateTypeMap() ?? [], []); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index 034ff1a6d54..e01f3be786e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -526,7 +526,7 @@ private static function analyzeNamedConstructor( $generic_param_types ); - $result_atomic_type->was_static = $from_static; + $result_atomic_type->is_static = $from_static; $statements_analyzer->node_data->setType( $stmt, @@ -553,7 +553,7 @@ private static function analyzeNamedConstructor( ) ); - $result_atomic_type->was_static = $from_static; + $result_atomic_type->is_static = $from_static; $statements_analyzer->node_data->setType( $stmt, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php index e6cb06ca161..dc6fa069753 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php @@ -158,7 +158,7 @@ public static function analyze( if ($first_part_lc === 'static') { $static_named_object = new TNamedObject($fq_class_name); - $static_named_object->was_static = true; + $static_named_object->is_static = true; $statements_analyzer->node_data->setType( $stmt, diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 75096d05a2e..cec41c93eea 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -628,7 +628,7 @@ private static function filterAtomicWithAnother( } if ($type_1_atomic instanceof TNamedObject) { - $type_1_atomic->was_static = false; + $type_1_atomic->is_static = false; } $atomic_comparison_results = new TypeComparisonResult(); diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index 881d34c2dc7..4d0f1e5f80d 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -301,8 +301,8 @@ public static function isContainedBy( if ($container_type_part instanceof TNamedObject && $input_type_part instanceof TNamedObject - && $container_type_part->was_static - && !$input_type_part->was_static + && $container_type_part->is_static + && !$input_type_part->is_static ) { if ($atomic_comparison_result) { $atomic_comparison_result->type_coerced = true; @@ -596,8 +596,8 @@ public static function isContainedBy( if ($container_type_part instanceof TNamedObject && $input_type_part instanceof TNamedObject - && $container_type_part->was_static - && !$input_type_part->was_static + && $container_type_part->is_static + && !$input_type_part->is_static ) { if ($atomic_comparison_result) { $atomic_comparison_result->type_coerced = true; diff --git a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php index c63f56dd8c2..971d52a555f 100644 --- a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php @@ -192,8 +192,8 @@ public static function isContainedBy( $allow_interface_equality ) || $param_comparison_result->type_coerced ) { - if ($container_param->hasFormerStaticObject() - && $input_param->isFormerStaticObject() + if ($container_param->hasStaticObject() + && $input_param->isStaticObject() && UnionTypeComparator::isContainedBy( $codebase, $input_param, diff --git a/src/Psalm/Internal/Type/Comparator/ObjectComparator.php b/src/Psalm/Internal/Type/Comparator/ObjectComparator.php index 43facc0cf49..5e3aff80828 100644 --- a/src/Psalm/Internal/Type/Comparator/ObjectComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ObjectComparator.php @@ -45,7 +45,7 @@ public static function isShallowlyContainedBy( } elseif ($intersection_container_type instanceof TTemplateParam) { $intersection_container_type_lower = null; } else { - $container_was_static = $intersection_container_type->was_static; + $container_was_static = $intersection_container_type->is_static; $intersection_container_type_lower = strtolower( $codebase->classlikes->getUnAliasedName( @@ -203,7 +203,7 @@ private static function isIntersectionShallowlyContainedBy( if ($intersection_container_type instanceof TNamedObject) { // this is extra check is redundant since we're comparing to a template as type - $intersection_container_type->was_static = false; + $intersection_container_type->is_static = false; } return UnionTypeComparator::isContainedBy( @@ -224,7 +224,7 @@ private static function isIntersectionShallowlyContainedBy( } elseif ($intersection_input_type instanceof TObjectWithProperties) { $intersection_input_type_lower = 'object'; } else { - $input_was_static = $intersection_input_type->was_static; + $input_was_static = $intersection_input_type->is_static; $intersection_input_type_lower = strtolower( $codebase->classlikes->getUnAliasedName( diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index 78bcae708fc..1e4269478c0 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -270,7 +270,7 @@ public static function combine( $generic_object = new TGenericObject($generic_type, $generic_type_params); if ($combination->object_static[$generic_type] ?? false) { - $generic_object->was_static = true; + $generic_object->is_static = true; } /** @psalm-suppress PropertyTypeCoercion */ @@ -513,11 +513,11 @@ private static function scrapeTypeProperties( if ($type instanceof TNamedObject) { if (array_key_exists($type->value, $combination->object_static)) { - if ($combination->object_static[$type->value] && !$type->was_static) { + if ($combination->object_static[$type->value] && !$type->is_static) { $combination->object_static[$type->value] = false; } } else { - $combination->object_static[$type->value] = $type->was_static; + $combination->object_static[$type->value] = $type->is_static; } } diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index 97c433680ee..7beaf7760dd 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -641,9 +641,9 @@ private static function expandNamedObject( } if (!$final && $return_type instanceof TNamedObject) { - $return_type->was_static = true; + $return_type->is_static = true; } - } elseif ($return_type->was_static + } elseif ($return_type->is_static && ($static_class_type instanceof TNamedObject || $static_class_type instanceof TTemplateParam) ) { @@ -661,9 +661,9 @@ private static function expandNamedObject( $return_type->extra_types[$extra_static_type->getKey()] = clone $extra_static_type; } } - } elseif ($return_type->was_static && is_string($static_class_type) && $final) { + } elseif ($return_type->is_static && is_string($static_class_type) && $final) { $return_type->value = $static_class_type; - $return_type->was_static = false; + $return_type->is_static = false; } elseif ($self_class && $return_type_lc === 'self') { $return_type->value = $self_class; } elseif ($parent_class && $return_type_lc === 'parent') { diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index 86200f348e0..2506fc2dce8 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -1084,7 +1084,7 @@ function (ParseTree $child_tree) use ($codebase, $template_type_map, $type_alias if ($intersect_static && $first_type instanceof TNamedObject ) { - $first_type->was_static = true; + $first_type->is_static = true; } if ($keyed_intersection_types) { diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index 8ba33ed81d5..e6e988f842a 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -127,10 +127,10 @@ public static function getStringFromFQCLN( array $aliased_classes, ?string $this_class, bool $allow_self = false, - bool $was_static = false + bool $is_static = false ): string { if ($allow_self && $value === $this_class) { - if ($was_static) { + if ($is_static) { return 'static'; } return 'self'; diff --git a/src/Psalm/Type/Atomic/GenericTrait.php b/src/Psalm/Type/Atomic/GenericTrait.php index 2d1ea1c1fd7..b633bbee642 100644 --- a/src/Psalm/Type/Atomic/GenericTrait.php +++ b/src/Psalm/Type/Atomic/GenericTrait.php @@ -64,7 +64,7 @@ public function getId(bool $nested = false): string ); } - if ($this->was_static) { + if ($this->is_static) { $extra_types .= '&static'; } } diff --git a/src/Psalm/Type/Atomic/TAnonymousClassInstance.php b/src/Psalm/Type/Atomic/TAnonymousClassInstance.php index 83189316e4e..5c066a03d8f 100644 --- a/src/Psalm/Type/Atomic/TAnonymousClassInstance.php +++ b/src/Psalm/Type/Atomic/TAnonymousClassInstance.php @@ -15,9 +15,9 @@ class TAnonymousClassInstance extends TNamedObject /** * @param string $value the name of the object */ - public function __construct(string $value, bool $was_static = false, ?string $extends = null) + public function __construct(string $value, bool $is_static = false, ?string $extends = null) { - parent::__construct($value, $was_static); + parent::__construct($value, $is_static); $this->extends = $extends; } diff --git a/src/Psalm/Type/Atomic/TNamedObject.php b/src/Psalm/Type/Atomic/TNamedObject.php index b922108c47e..07e725f86ac 100644 --- a/src/Psalm/Type/Atomic/TNamedObject.php +++ b/src/Psalm/Type/Atomic/TNamedObject.php @@ -27,7 +27,7 @@ class TNamedObject extends Atomic /** * @var bool */ - public $was_static = false; + public $is_static = false; /** * Whether or not this type can represent a child of the class named in $value @@ -38,14 +38,14 @@ class TNamedObject extends Atomic /** * @param string $value the name of the object */ - public function __construct(string $value, bool $was_static = false, bool $definite_class = false) + public function __construct(string $value, bool $is_static = false, bool $definite_class = false) { if ($value[0] === '\\') { $value = substr($value, 1); } $this->value = $value; - $this->was_static = $was_static; + $this->is_static = $is_static; $this->definite_class = $definite_class; } @@ -75,7 +75,7 @@ public function getId(bool $nested = false): string ); } - return $this->was_static ? $this->value . '&static' : $this->value; + return $this->is_static ? $this->value . '&static' : $this->value; } /** @@ -105,7 +105,7 @@ public function toNamespacedString( $aliased_classes, $this_class, true, - $this->was_static + $this->is_static ) . $intersection_types; } @@ -122,7 +122,7 @@ public function toPhpString( return $analysis_php_version_id >= 8_00_00 ? 'static' : null; } - if ($this->was_static && $this->value === $this_class) { + if ($this->is_static && $this->value === $this_class) { return $analysis_php_version_id >= 8_00_00 ? 'static' : 'self'; } @@ -136,7 +136,7 @@ public function toPhpString( public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { - return ($this->value !== 'static' && $this->was_static === false) || $analysis_php_version_id >= 8_00_00; + return ($this->value !== 'static' && $this->is_static === false) || $analysis_php_version_id >= 8_00_00; } public function replaceTemplateTypesWithArgTypes( diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index bd9436f390b..0388ca6e077 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -763,11 +763,11 @@ public function hasNamedObjectType(): bool return false; } - public function isFormerStaticObject(): bool + public function isStaticObject(): bool { foreach ($this->types as $type) { if (!$type instanceof TNamedObject - || !$type->was_static + || !$type->is_static ) { return false; } @@ -776,11 +776,11 @@ public function isFormerStaticObject(): bool return true; } - public function hasFormerStaticObject(): bool + public function hasStaticObject(): bool { foreach ($this->types as $type) { if ($type instanceof TNamedObject - && $type->was_static + && $type->is_static ) { return true; } @@ -926,7 +926,7 @@ public function hasTemplateOrStatic(): bool $this->types, fn(Atomic $type): bool => $type instanceof TTemplateParam || ($type instanceof TNamedObject - && ($type->was_static + && ($type->is_static || ($type->extra_types && array_filter( $type->extra_types, From 7dc1c454fb8baaaf94a1c76c48287a7dfb1f47bb Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sun, 9 Jan 2022 15:35:19 -0500 Subject: [PATCH 75/78] =?UTF-8?q?Don=E2=80=99t=20perform=20containedness?= =?UTF-8?q?=20check=20twice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Internal/Type/Comparator/GenericTypeComparator.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php index 971d52a555f..8758bd701a8 100644 --- a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php @@ -194,15 +194,6 @@ public static function isContainedBy( ) { if ($container_param->hasStaticObject() && $input_param->isStaticObject() - && UnionTypeComparator::isContainedBy( - $codebase, - $input_param, - $container_param, - $container_param->ignore_nullable_issues, - $container_param->ignore_falsable_issues, - $param_comparison_result, - $allow_interface_equality - ) ) { // do nothing } else { From 6589ad1504341df14a47edc000d14e6f92f69f92 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 9 Jan 2022 22:51:23 +0200 Subject: [PATCH 76/78] Document BC breaks in #7358 --- UPGRADING.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 70bf8292114..a389e609676 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -115,6 +115,12 @@ - `Psalm\Type\Union` - While not a BC break per se, all classes / interfaces / traits / enums under `Psalm\Internal` namespace are now marked `@internal`. + - [BC] Parameter 1 of `Psalm\Type\Atomic\TNamedObject::__construct()` changed name from `was_static` to `is_static` + - [BC] Parameter 1 of `Psalm\Type\Atomic\TAnonymousClassInstance::__construct()` changed name from `was_static` to `is_static` + - [BC] Parameter 5 of `Psalm\Type::getStringFromFQCLN()` changed name from `was_static` to `is_static` + - [BC] Property `Psalm\Type\Atomic\TNamedObject::$was_static` was renamed to `$is_static` + - [BC] Method `Psalm\Type\Union::isFormerStaticObject()` was renamed to `isStaticObject()` + - [BC] Method `Psalm\Type\Union::hasFormerStaticObject()` was renamed to `hasStaticObject()` ## Removed - [BC] Property `Psalm\Codebase::$php_major_version` was removed, use @@ -156,5 +162,3 @@ - [BC] Method `Psalm\Issue\CodeIssue::getMessage()` was removed - [BC] Method `Psalm\DocComment::parse()` was removed - [BC] Class `Psalm\Type\Atomic\THtmlEscapedString` has been removed - - From 72fd3de8861e69aa42f21a413e163f0b7a29a28e Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sun, 9 Jan 2022 16:19:04 -0500 Subject: [PATCH 77/78] Simplify return type for TypeExpander::expandAtomic --- .../Call/ClassTemplateParamCollector.php | 8 +- .../Expression/Fetch/ArrayFetchAnalyzer.php | 20 ++--- .../Internal/Type/AssertionReconciler.php | 27 ++----- .../Type/Comparator/UnionTypeComparator.php | 6 -- src/Psalm/Internal/Type/TypeExpander.php | 76 ++++++++----------- .../Internal/TypeVisitor/TypeChecker.php | 5 +- 6 files changed, 47 insertions(+), 95 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php index 3f05a2a2e67..094286c8d5c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php @@ -268,12 +268,8 @@ private static function expandType( true ); - if ($expanded instanceof Atomic) { - $output_type_extends[] = $expanded; - } else { - foreach ($expanded as $type) { - $output_type_extends[] = $type; - } + foreach ($expanded as $type) { + $output_type_extends[] = $type; } } else { $output_type_extends[] = $type_extends_atomic; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index 88fab03072c..90675e5d728 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -2007,21 +2007,11 @@ private static function checkArrayOffsetType( true ); - if ($expanded instanceof Atomic) { - if (!$expanded instanceof TClassConstant) { - $has_valid_absolute_offset = self::checkArrayOffsetType( - $offset_type, - [$expanded], - $codebase - ); - } - } else { - $has_valid_absolute_offset = self::checkArrayOffsetType( - $offset_type, - $expanded, - $codebase - ); - } + $has_valid_absolute_offset = self::checkArrayOffsetType( + $offset_type, + $expanded, + $codebase + ); if ($has_valid_absolute_offset) { break; diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index cec41c93eea..a6ed2272950 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -1250,31 +1250,20 @@ private static function handleLiteralEqualityWithInt( true ); - if ($expanded instanceof Atomic) { - $compatible_int_type = self::getCompatibleIntType($existing_var_type, $value, $is_loose_equality); + foreach ($expanded as $expanded_type) { + $compatible_int_type = self::getCompatibleIntType( + $existing_var_type, + $value, + $is_loose_equality + ); + if ($compatible_int_type !== null) { return $compatible_int_type; } - if ($expanded instanceof TInt) { + if ($expanded_type instanceof TInt) { $has_int = true; } - } else { - foreach ($expanded as $expanded_type) { - $compatible_int_type = self::getCompatibleIntType( - $existing_var_type, - $value, - $is_loose_equality - ); - - if ($compatible_int_type !== null) { - return $compatible_int_type; - } - - if ($expanded_type instanceof TInt) { - $has_int = true; - } - } } } } diff --git a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php index c997199d650..288f3e25845 100644 --- a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php @@ -484,12 +484,6 @@ private static function getTypeParts( true, true ); - if ($expanded instanceof Atomic) { - if (!$expanded instanceof TTypeAlias && !$expanded instanceof TClassConstant) { - $atomic_types[] = $expanded; - } - continue; - } array_push($atomic_types, ...$expanded); } diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index 7beaf7760dd..a26d37dcd32 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -42,7 +42,6 @@ use function array_values; use function count; use function get_class; -use function is_array; use function is_string; use function reset; use function strpos; @@ -74,7 +73,7 @@ public static function expandUnion( $new_return_type_parts = []; - $has_array_output = false; + $had_split_values = false; foreach ($return_type->getAtomicTypes() as $return_type_part) { $parts = self::expandAtomic( @@ -90,15 +89,14 @@ public static function expandUnion( $expand_templates ); - if (is_array($parts)) { - $new_return_type_parts = array_merge($new_return_type_parts, $parts); - $has_array_output = true; - } else { - $new_return_type_parts[] = $parts; + if ($return_type_part instanceof TTypeAlias || count($parts) > 1) { + $had_split_values = true; } + + $new_return_type_parts = array_merge($new_return_type_parts, $parts); } - if ($has_array_output) { + if ($had_split_values) { $fleshed_out_type = TypeCombiner::combine( $new_return_type_parts, $codebase @@ -125,7 +123,7 @@ public static function expandUnion( /** * @param string|TNamedObject|TTemplateParam|null $static_class_type * - * @return Atomic|non-empty-list + * @return non-empty-list */ public static function expandAtomic( Codebase $codebase, @@ -138,7 +136,7 @@ public static function expandAtomic( bool $final = false, bool $expand_generic = false, bool $expand_templates = false - ) { + ): array { if ($return_type instanceof TNamedObject || $return_type instanceof TTemplateParam ) { @@ -239,7 +237,7 @@ public static function expandAtomic( if ($evaluate_class_constants && $codebase->classOrInterfaceOrEnumExists($return_type->fq_classlike_name)) { if (strtolower($return_type->const_name) === 'class') { - return new TLiteralClassString($return_type->fq_classlike_name); + return [new TLiteralClassString($return_type->fq_classlike_name)]; } $class_storage = $codebase->classlike_storage_provider->get($return_type->fq_classlike_name); @@ -293,7 +291,7 @@ public static function expandAtomic( } } - return $return_type; + return [$return_type]; } if ($return_type instanceof TTypeAlias) { @@ -317,7 +315,7 @@ public static function expandAtomic( $recursively_fleshed_out_types = []; foreach ($replacement_atomic_types as $replacement_atomic_type) { - $recursively_fleshed_out_type = self::expandAtomic( + $more_recursively_fleshed_out_types = self::expandAtomic( $codebase, $replacement_atomic_type, $self_class, @@ -330,14 +328,10 @@ public static function expandAtomic( $expand_templates ); - if (is_array($recursively_fleshed_out_type)) { - $recursively_fleshed_out_types = array_merge( - $recursively_fleshed_out_type, - $recursively_fleshed_out_types - ); - } else { - $recursively_fleshed_out_types[] = $recursively_fleshed_out_type; - } + $recursively_fleshed_out_types = array_merge( + $more_recursively_fleshed_out_types, + $recursively_fleshed_out_types + ); } return $recursively_fleshed_out_types; @@ -345,7 +339,7 @@ public static function expandAtomic( } } - return $return_type; + return [$return_type]; } if ($return_type instanceof TKeyOfClassConstant @@ -385,12 +379,12 @@ public static function expandAtomic( } } - return $return_type; + return [$return_type]; } if ($return_type instanceof TIntMask) { if (!$evaluate_class_constants) { - return new TInt(); + return [new TInt()]; } $potential_ints = []; @@ -409,12 +403,10 @@ public static function expandAtomic( $expand_templates ); - if (is_array($new_value_type)) { - $new_value_type = reset($new_value_type); - } + $new_value_type = reset($new_value_type); if (!$new_value_type instanceof TLiteralInt) { - return new TInt(); + return [new TInt()]; } $potential_ints[] = $new_value_type->value; @@ -425,7 +417,7 @@ public static function expandAtomic( if ($return_type instanceof TIntMaskOf) { if (!$evaluate_class_constants) { - return new TInt(); + return [new TInt()]; } $value_type = $return_type->value; @@ -443,15 +435,11 @@ public static function expandAtomic( $expand_templates ); - if (!is_array($new_value_types)) { - return new TInt(); - } - $potential_ints = []; foreach ($new_value_types as $new_value_type) { if (!$new_value_type instanceof TLiteralInt) { - return new TInt(); + return [new TInt()]; } $potential_ints[] = $new_value_type->value; @@ -578,7 +566,7 @@ public static function expandAtomic( ); } - return $return_type; + return [$return_type]; } /** @@ -678,7 +666,7 @@ private static function expandNamedObject( /** * @param string|TNamedObject|TTemplateParam|null $static_class_type * - * @return Atomic|non-empty-list + * @return non-empty-list */ private static function expandConditional( Codebase $codebase, @@ -691,7 +679,7 @@ private static function expandConditional( bool $final = false, bool $expand_generic = false, bool $expand_templates = false - ) { + ): array { $new_as_type = self::expandUnion( $codebase, $return_type->as_type, @@ -725,8 +713,8 @@ private static function expandConditional( $expand_templates ); - if (!is_array($candidate)) { - $assertion = $candidate->getAssertionString(); + if (count($candidate) === 1) { + $assertion = $candidate[0]->getAssertionString(); } } } @@ -734,7 +722,7 @@ private static function expandConditional( $if_conditional_return_types = []; foreach ($return_type->if_type->getAtomicTypes() as $if_atomic_type) { - $candidate = self::expandAtomic( + $candidate_types = self::expandAtomic( $codebase, $if_atomic_type, $self_class, @@ -747,8 +735,6 @@ private static function expandConditional( $expand_templates ); - $candidate_types = is_array($candidate) ? $candidate : [$candidate]; - $if_conditional_return_types = array_merge( $if_conditional_return_types, $candidate_types @@ -758,7 +744,7 @@ private static function expandConditional( $else_conditional_return_types = []; foreach ($return_type->else_type->getAtomicTypes() as $else_atomic_type) { - $candidate = self::expandAtomic( + $candidate_types = self::expandAtomic( $codebase, $else_atomic_type, $self_class, @@ -771,8 +757,6 @@ private static function expandConditional( $expand_templates ); - $candidate_types = is_array($candidate) ? $candidate : [$candidate]; - $else_conditional_return_types = array_merge( $else_conditional_return_types, $candidate_types @@ -892,6 +876,6 @@ private static function expandConditional( $expand_templates ); - return $return_type; + return [$return_type]; } } diff --git a/src/Psalm/Internal/TypeVisitor/TypeChecker.php b/src/Psalm/Internal/TypeVisitor/TypeChecker.php index fbf9906bbac..4eaaeb18987 100644 --- a/src/Psalm/Internal/TypeVisitor/TypeChecker.php +++ b/src/Psalm/Internal/TypeVisitor/TypeChecker.php @@ -34,7 +34,6 @@ use function array_keys; use function array_search; use function count; -use function is_array; use function md5; use function strpos; use function strtolower; @@ -324,7 +323,7 @@ public function checkScalarClassConstant(TClassConstant $atomic): void $const_name = $atomic->const_name; if (strpos($const_name, '*') !== false) { - $expanded = TypeExpander::expandAtomic( + TypeExpander::expandAtomic( $this->source->getCodebase(), $atomic, $fq_classlike_name, @@ -334,7 +333,7 @@ public function checkScalarClassConstant(TClassConstant $atomic): void true ); - $is_defined = is_array($expanded) && count($expanded) > 0; + $is_defined = true; } else { $class_constant_type = $this->source->getCodebase()->classlikes->getClassConstantType( $fq_classlike_name, From 7f8601489dd806a792038091345382899be1ea55 Mon Sep 17 00:00:00 2001 From: Andrew Nagy Date: Mon, 10 Jan 2022 23:27:18 +0000 Subject: [PATCH 78/78] Fix closure to have storage bug in codeAction --- .../Internal/LanguageServer/LanguageServer.php | 16 ++++++++++++++++ .../LanguageServer/Server/TextDocument.php | 11 ++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index 6716f2024ac..b78e41d3a30 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -103,6 +103,11 @@ class LanguageServer extends Dispatcher */ protected $onchange_paths_to_analyze = []; + /** + * @var array> + */ + protected $current_issues = []; + public function __construct( ProtocolReader $reader, ProtocolWriter $writer, @@ -365,6 +370,7 @@ public function doAnalysis(): void public function emitIssues(array $uris): void { $data = IssueBuffer::clear(); + $this->current_issues = $data; foreach ($uris as $file_path => $uri) { $diagnostics = array_map( @@ -560,4 +566,14 @@ public static function uriToPath(string $uri): string return $filepath; } + + /** + * Get the value of current_issues + * + * @return array> + */ + public function getCurrentIssues(): array + { + return $this->current_issues; + } } diff --git a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php index cda24184534..2d232906c4f 100644 --- a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php +++ b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php @@ -24,7 +24,6 @@ use Psalm\Exception\UnanalyzedFileException; use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\LanguageServer\LanguageServer; -use Psalm\IssueBuffer; use UnexpectedValueException; use function array_combine; @@ -367,9 +366,15 @@ public function codeAction(TextDocumentIdentifier $textDocument, Range $range): $this->codebase->analyzer->addFilesToAnalyze( array_combine($all_file_paths_to_analyze, $all_file_paths_to_analyze) ); - $this->codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false); - $issues = IssueBuffer::clear(); + try { + $this->codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false); + } catch (UnexpectedValueException $e) { + error_log('codeAction errored on file ' . $file_path. ', Reason: '.$e->getMessage()); + return new Success(null); + } + + $issues = $this->server->getCurrentIssues(); if (empty($issues[$file_path])) { return new Success(null);