From e8b47f7d42d78056fcb889b475b587dcb7f9fd2a Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 4 Feb 2024 18:52:20 +0100 Subject: [PATCH 1/2] Do not add `callable` as a native property type It's invalid in all PHP versions: https://3v4l.org/bXWo2 Also see php.net/manual/en/language.types.declarations.php#language.types.declarations.base.function Fixes vimeo/psalm#10650 --- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 4 ++- .../MissingPropertyTypeTest.php | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 3fcaa310d48..d40c86b2c90 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1649,7 +1649,9 @@ private static function addOrUpdatePropertyType( $allow_native_type = !$docblock_only && $codebase->analysis_php_version_id >= 7_04_00 - && $codebase->allow_backwards_incompatible_changes; + && $codebase->allow_backwards_incompatible_changes + && !$inferred_type->hasCallableType() // PHP does not support callable properties + ; $manipulator->setType( $allow_native_type diff --git a/tests/FileManipulation/MissingPropertyTypeTest.php b/tests/FileManipulation/MissingPropertyTypeTest.php index afeb644ee88..5e0db2d6c4d 100644 --- a/tests/FileManipulation/MissingPropertyTypeTest.php +++ b/tests/FileManipulation/MissingPropertyTypeTest.php @@ -294,6 +294,42 @@ public function bar() { 'issues_to_fix' => ['MissingPropertyType'], 'safe_types' => true, ], + 'doNotAddCallablePropertyTypes' => [ + 'input' => <<<'PHP' + u = $u; + $this->v = $v; + } + } + PHP, + 'output' => <<<'PHP' + u = $u; + $this->v = $v; + } + } + PHP, + 'php_version' => '7.4', + 'issues_to_fix' => ['MissingPropertyType'], + 'safe_types' => true, + ], ]; } } From b2a2cd7884d9c92cebfb44e71516658b2ab64771 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 4 Feb 2024 19:16:42 +0100 Subject: [PATCH 2/2] Allow adding `Closure` as a native property type --- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 4 +++- src/Psalm/Type/Atomic/TClosure.php | 3 ++- .../MissingPropertyTypeTest.php | 23 +++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index d40c86b2c90..a584d9f0f97 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1650,7 +1650,9 @@ private static function addOrUpdatePropertyType( $allow_native_type = !$docblock_only && $codebase->analysis_php_version_id >= 7_04_00 && $codebase->allow_backwards_incompatible_changes - && !$inferred_type->hasCallableType() // PHP does not support callable properties + // PHP does not support callable properties, but does allow Closure properties + // hasCallableType() treats Closure as a callable, but getCallableTypes() does not + && $inferred_type->getCallableTypes() === [] ; $manipulator->setType( diff --git a/src/Psalm/Type/Atomic/TClosure.php b/src/Psalm/Type/Atomic/TClosure.php index 94ee9446d44..07d6740cbec 100644 --- a/src/Psalm/Type/Atomic/TClosure.php +++ b/src/Psalm/Type/Atomic/TClosure.php @@ -50,7 +50,8 @@ public function __construct( public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { - return false; + // it can, if it's just 'Closure' + return $this->params === null && $this->return_type === null && $this->is_pure === null; } /** diff --git a/tests/FileManipulation/MissingPropertyTypeTest.php b/tests/FileManipulation/MissingPropertyTypeTest.php index 5e0db2d6c4d..f32d8c2562c 100644 --- a/tests/FileManipulation/MissingPropertyTypeTest.php +++ b/tests/FileManipulation/MissingPropertyTypeTest.php @@ -330,6 +330,29 @@ public function __construct(?callable $u, callable $v) { 'issues_to_fix' => ['MissingPropertyType'], 'safe_types' => true, ], + 'addClosurePropertyType' => [ + 'input' => <<<'PHP' + u = $u; + } + } + PHP, + 'output' => <<<'PHP' + u = $u; + } + } + PHP, + 'php_version' => '7.4', + 'issues_to_fix' => ['MissingPropertyType'], + 'safe_types' => true, + ], ]; } }