From 70f2fb7d1b0511cd87d9474f981cc0c24317020b Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Mon, 23 Jan 2023 05:16:12 -0400 Subject: [PATCH] Check `$this` in static closures Fixes vimeo/psalm#9148 Fixes vimeo/psalm#2813 --- .../Internal/Analyzer/ClosureAnalyzer.php | 2 +- .../Analyzer/FunctionLikeAnalyzer.php | 4 ++- .../Reflector/FunctionLikeNodeScanner.php | 2 ++ src/Psalm/Storage/FunctionStorage.php | 6 ++++ tests/ClosureTest.php | 28 +++++++++++++++++++ 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php b/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php index 97930b24e..47b1af371 100644 --- a/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php @@ -84,7 +84,7 @@ class ClosureAnalyzer extends FunctionLikeAnalyzer $codebase = $statements_analyzer->getCodebase(); - if (!$statements_analyzer->isStatic()) { + if (!$statements_analyzer->isStatic() && !$closure_analyzer->isStatic()) { if ($context->collect_mutations && $context->self && $codebase->classExtends( diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index d86a5422d..77c4ff55f 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -217,7 +217,9 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer $this->suppressed_issues += $appearing_class_storage->suppressed_issues; } - if ($storage instanceof MethodStorage && $storage->is_static) { + if (($storage instanceof MethodStorage || $storage instanceof FunctionStorage) + && $storage->is_static + ) { $this->is_static = true; } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index 494c888a0..ce008167b 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -1120,6 +1120,8 @@ class FunctionLikeNodeScanner $storage = $this->storage = $this->file_storage->functions[$function_id] = new FunctionStorage(); + $storage->is_static = $stmt->static; + if ($stmt instanceof PhpParser\Node\Expr\Closure) { foreach ($stmt->uses as $closure_use) { if ($closure_use->byRef && is_string($closure_use->var->name)) { diff --git a/src/Psalm/Storage/FunctionStorage.php b/src/Psalm/Storage/FunctionStorage.php index e39e7cc23..813730f56 100644 --- a/src/Psalm/Storage/FunctionStorage.php +++ b/src/Psalm/Storage/FunctionStorage.php @@ -6,4 +6,10 @@ final class FunctionStorage extends FunctionLikeStorage { /** @var array */ public $byref_uses = []; + + /** + * @var bool + * @todo lift this property to FunctionLikeStorage in Psalm 6 + */ + public $is_static = false; } diff --git a/tests/ClosureTest.php b/tests/ClosureTest.php index 6fff95861..34bac3dc5 100644 --- a/tests/ClosureTest.php +++ b/tests/ClosureTest.php @@ -1368,6 +1368,34 @@ class ClosureTest extends TestCase 'ignored_issues' => [], 'php_version' => '8.1', ], + 'thisInStaticClosure' => [ + 'code' => 'a; + }; + $f(); + } + } + ', + 'error_message' => 'InvalidScope', + ], + 'thisInStaticArrowFunction' => [ + 'code' => ' $this->a; + return $f();; + } + } + ', + 'error_message' => 'InvalidScope', + 'ignored_issues' => [], + 'php_version' => '7.4', + ], ]; } }