From 1b0220bca3840a910ca3bd82126247b485e16f15 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 30 Oct 2024 16:36:16 +0100 Subject: [PATCH 1/2] More precise types in immediately invoked callables --- bug-11561.php | 39 ++++++++++++++++++ conf/config.neon | 5 +++ src/Analyser/MutatingScope.php | 6 ++- src/Analyser/NodeScopeResolver.php | 6 +-- .../ImmediatelyInvokedClosureVisitor.php | 22 ++++++++++ tests/PHPStan/Analyser/nsrt/bug-11561.php | 40 +++++++++++++++++++ 6 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 bug-11561.php create mode 100644 src/Parser/ImmediatelyInvokedClosureVisitor.php create mode 100644 tests/PHPStan/Analyser/nsrt/bug-11561.php diff --git a/bug-11561.php b/bug-11561.php new file mode 100644 index 0000000000..5eea12130e --- /dev/null +++ b/bug-11561.php @@ -0,0 +1,39 @@ +getVariableType($variableName); if (!$variableType->equals($prevVariableType)) { $variableType = TypeCombinator::union($variableType, $prevVariableType); - $variableType = self::generalizeType($variableType, $prevVariableType, 0); + if ($expr->getAttribute(ImmediatelyInvokedClosureVisitor::ATTRIBUTE_NAME) !== true) { + $variableType = self::generalizeType($variableType, $prevVariableType, 0); + } } } diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 5831cdaaaf..fd6ac13fc3 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -4232,7 +4232,7 @@ private function processClosureNode( } $closureScope = $scope->enterAnonymousFunction($expr, $callableParameters); - $closureScope = $closureScope->processClosureScope($scope, null, $byRefUses); + $closureScope = $closureScope->processClosureScope($expr, $scope, null, $byRefUses); $closureType = $closureScope->getAnonymousFunctionReflection(); if (!$closureType instanceof ClosureType) { throw new ShouldNotHappenException(); @@ -4302,7 +4302,7 @@ private function processClosureNode( $intermediaryClosureScope = $intermediaryClosureScope->mergeWith($exitPoint->getScope()); } $closureScope = $scope->enterAnonymousFunction($expr, $callableParameters); - $closureScope = $closureScope->processClosureScope($intermediaryClosureScope, $prevScope, $byRefUses); + $closureScope = $closureScope->processClosureScope($expr, $intermediaryClosureScope, $prevScope, $byRefUses); if ($closureScope->equals($prevScope)) { break; } @@ -4322,7 +4322,7 @@ private function processClosureNode( array_merge($statementResult->getImpurePoints(), $closureImpurePoints), ), $closureScope); - return new ProcessClosureResult($scope->processClosureScope($closureScope, null, $byRefUses), $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions); + return new ProcessClosureResult($scope->processClosureScope($expr, $closureScope, null, $byRefUses), $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions); } /** diff --git a/src/Parser/ImmediatelyInvokedClosureVisitor.php b/src/Parser/ImmediatelyInvokedClosureVisitor.php new file mode 100644 index 0000000000..c77059e214 --- /dev/null +++ b/src/Parser/ImmediatelyInvokedClosureVisitor.php @@ -0,0 +1,22 @@ +name instanceof Node\Expr\Closure) { + $node->name->setAttribute(self::ATTRIBUTE_NAME, true); + } + + return null; + } + +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-11561.php b/tests/PHPStan/Analyser/nsrt/bug-11561.php new file mode 100644 index 0000000000..1a01e5f97a --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-11561.php @@ -0,0 +1,40 @@ += 8.0 + +namespace Bug11561; + +use function PHPStan\Testing\assertType; +use DateTime; + +/** @param array{date: DateTime} $c */ +function main(mixed $c): void{ + assertType('array{date: DateTime}', $c); + $c['id']=1; + assertType('array{date: DateTime, id: 1}', $c); + + $x = (function() use (&$c) { + assertType("array{date: DateTime, id: 1, name?: 'ruud'}", $c); + $c['name'] = 'ruud'; + assertType("array{date: DateTime, id: 1, name: 'ruud'}", $c); + return 'x'; + })(); + + assertType("array{date: DateTime, id: 1, name?: 'ruud'}", $c); +} + + +/** @param array{date: DateTime} $c */ +function main2(mixed $c): void{ + assertType('array{date: DateTime}', $c); + $c['id']=1; + $c['name'] = 'staabm'; + assertType("array{date: DateTime, id: 1, name: 'staabm'}", $c); + + $x = (function() use (&$c) { + assertType("array{date: DateTime, id: 1, name: 'ruud'|'staabm'}", $c); + $c['name'] = 'ruud'; + assertType("array{date: DateTime, id: 1, name: 'ruud'}", $c); + return 'x'; + })(); + + assertType("array{date: DateTime, id: 1, name: 'ruud'|'staabm'}", $c); +} From 27da7125cd14b4f192db9e3873a6bf8a0fc8f656 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 30 Oct 2024 20:03:00 +0100 Subject: [PATCH 2/2] Delete bug-11561.php --- bug-11561.php | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 bug-11561.php diff --git a/bug-11561.php b/bug-11561.php deleted file mode 100644 index 5eea12130e..0000000000 --- a/bug-11561.php +++ /dev/null @@ -1,39 +0,0 @@ -