From 73bad8967d874a8a7f2175df303927dda8010e9b Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 19 Feb 2023 04:18:32 -0400 Subject: [PATCH] Forbid first-class callables in `new` It's an invalid construct in PHP, not rejected by PHPParser for some reaason. Fixes vimeo/psalm#9335 --- .../Analyzer/Statements/Expression/Call/NewAnalyzer.php | 9 +++++++++ tests/ClosureTest.php | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index c02cb9304..0087a5850 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -29,6 +29,7 @@ use Psalm\Issue\InternalClass; use Psalm\Issue\InternalMethod; use Psalm\Issue\InvalidStringClass; use Psalm\Issue\MixedMethodCall; +use Psalm\Issue\ParseError; use Psalm\Issue\TooManyArguments; use Psalm\Issue\UndefinedClass; use Psalm\Issue\UnsafeGenericInstantiation; @@ -84,6 +85,14 @@ class NewAnalyzer extends CallAnalyzer $from_static = false; + if ($stmt->isFirstClassCallable()) { + IssueBuffer::maybeAdd(new ParseError( + 'First-class callables cannot be used in new', + new CodeLocation($statements_analyzer->getSource(), $stmt), + )); + return false; + } + if ($stmt->class instanceof PhpParser\Node\Name) { if (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) { $aliases = $statements_analyzer->getAliases(); diff --git a/tests/ClosureTest.php b/tests/ClosureTest.php index 5a381eeb3..43d441944 100644 --- a/tests/ClosureTest.php +++ b/tests/ClosureTest.php @@ -1419,6 +1419,15 @@ class ClosureTest extends TestCase 'ignored_issues' => [], 'php_version' => '7.4', ], + 'FirstClassCallable:WithNew' => [ + 'code' => <<<'PHP' + 'ParseError', + 'ignored_issues' => [], + 'php_version' => '8.1', + ], ]; } }