Skip to content

Commit 7ef7bea

Browse files
ondrejmirtesclaude
andcommitted
Adopt the upstream gatherer frames
The rebase over the merged gathering cleanup (#6258) kept the branch's wrapper-based gatherer sites while the base deleted GatheringNodeCallback; this converts them to the upstream frame mechanism - pushNodeGatherer()/popNodeGatherer() on NodeScopeResolver, fed the raw walk scope by callNodeCallback() and per replayed pair by replayRecording() - woven into the branch's shapes: the per-body storages of method/function bodies, the ambient storage pushes, and the scope-bound replayRecording() signature. Gatherer bodies are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GnwgpaeUXRkgSDyg95tfK8
1 parent e237e79 commit 7ef7bea

6 files changed

Lines changed: 137 additions & 45 deletions

File tree

‎src/Analyser/ExprHandler/FuncCallHandler.php‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
use PHPStan\Analyser\ExprHandler\Helper\DynamicReturnTypeStoragePrimer;
2424
use PHPStan\Analyser\ExprHandler\Helper\EarlyTerminatingCallHelper;
2525
use PHPStan\Analyser\ExprHandler\Helper\FuncCallScopeEffectsHelper;
26-
use PHPStan\Analyser\GatheringNodeCallback;
2726
use PHPStan\Analyser\ImpurePoint;
2827
use PHPStan\Analyser\InternalThrowPoint;
2928
use PHPStan\Analyser\MutatingScope;
@@ -230,7 +229,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
230229
/** @var array{Type, Type}|null $arrayWalkValueTypes */
231230
$arrayWalkValueTypes = null;
232231
$arrayWalkArrayArg = null;
233-
$nodeCallbackForArgs = $nodeCallback;
232+
$argsGatherer = null;
234233
if (
235234
$functionReflection !== null
236235
&& $functionReflection->getName() === 'array_walk'
@@ -252,7 +251,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
252251
if ($firstParamName !== null) {
253252
$arrayWalkArrayArg = $normalizedExpr->getArgs()[0]->value;
254253

255-
$nodeCallbackForArgs = new GatheringNodeCallback(static function (Node $node, Scope $scope) use ($callbackArg, $firstParamName, &$arrayWalkValueTypes): void {
254+
$argsGatherer = static function (Node $node, Scope $scope) use ($callbackArg, $firstParamName, &$arrayWalkValueTypes): void {
256255
if (!($node instanceof ClosureReturnStatementsNode) || $node->getClosureExpr() !== $callbackArg) {
257256
return;
258257
}
@@ -284,12 +283,21 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
284283
TypeCombinator::union(...$types),
285284
TypeCombinator::union(...$nativeTypes),
286285
];
287-
}, $nodeCallback);
286+
};
288287
}
289288
}
290289

291290
$scopeBeforeArgs = $scope;
292-
$argsResult = $nodeScopeResolver->processArgs($stmt, $functionReflection, null, $variants, $namedArgumentsVariants, $normalizedExpr, $scope, $storage, $nodeCallbackForArgs, $context);
291+
if ($argsGatherer !== null) {
292+
$nodeScopeResolver->pushNodeGatherer($argsGatherer);
293+
}
294+
try {
295+
$argsResult = $nodeScopeResolver->processArgs($stmt, $functionReflection, null, $variants, $namedArgumentsVariants, $normalizedExpr, $scope, $storage, $nodeCallback, $context);
296+
} finally {
297+
if ($argsGatherer !== null) {
298+
$nodeScopeResolver->popNodeGatherer();
299+
}
300+
}
293301
$resolvedParametersAcceptor = $argsResult->getResolvedParametersAcceptor();
294302
$scope = $argsResult->getScope();
295303
$nodeScopeResolver->processDroppedArgs($stmt, $expr, $normalizedExpr, $scope, $storage, $context);

‎src/Analyser/ExprHandler/NewHandler.php‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use PHPStan\Analyser\ExprHandler;
1818
use PHPStan\Analyser\ExprHandler\Helper\DefaultNarrowingHelper;
1919
use PHPStan\Analyser\ExprHandler\Helper\DynamicReturnTypeStoragePrimer;
20-
use PHPStan\Analyser\GatheringNodeCallback;
2120
use PHPStan\Analyser\ImpurePoint;
2221
use PHPStan\Analyser\InternalThrowPoint;
2322
use PHPStan\Analyser\MutatingScope;
@@ -139,7 +138,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
139138

140139
if ($constructorReflection->getDeclaringClass()->getName() === $classReflection->getName()) {
141140
$constructorResult = null;
142-
$nodeScopeResolver->processStmtNode($expr->class, $scope, $storage, new GatheringNodeCallback(static function (Node $node, Scope $scope) use ($classReflection, &$constructorResult): void {
141+
$nodeScopeResolver->pushNodeGatherer(static function (Node $node, Scope $scope) use ($classReflection, &$constructorResult): void {
143142
if (!$node instanceof MethodReturnStatementsNode) {
144143
return;
145144
}
@@ -157,7 +156,13 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
157156
return;
158157
}
159158
$constructorResult = $node;
160-
}, $nodeCallback), StatementContext::createTopLevel());
159+
});
160+
try {
161+
$nodeScopeResolver->processStmtNode($expr->class, $scope, $storage, $nodeCallback, StatementContext::createTopLevel());
162+
} finally {
163+
$nodeScopeResolver->popNodeGatherer();
164+
}
165+
161166
if ($constructorResult !== null) {
162167
$throwPoints = array_map(static fn (ThrowPoint $point): InternalThrowPoint => InternalThrowPoint::createFromPublic($point, $scope), $constructorResult->getStatementResult()->getThrowPoints());
163168
$impurePoints = $constructorResult->getImpurePoints();

‎src/Analyser/NodeScopeResolver.php‎

Lines changed: 94 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
use function array_last;
106106
use function array_map;
107107
use function array_merge;
108+
use function array_pop;
108109
use function array_slice;
109110
use function array_values;
110111
use function count;
@@ -143,6 +144,17 @@ class NodeScopeResolver
143144
*/
144145
private bool $consumeStoredExpressionResults = false;
145146

147+
/**
148+
* Engine-feeding gatherer frames (return statements, execution ends,
149+
* impure points, ...), innermost last. callNodeCallback() feeds every
150+
* frame the raw walk scope at the emission position - gatherers are
151+
* engine code and never ask about types, and their arrays are read as
152+
* soon as the enclosing body walk returns.
153+
*
154+
* @var list<callable(Node, Scope): void>
155+
*/
156+
private array $nodeGatherers = [];
157+
146158
/** Whether the PHPSTAN_GUARD_NW diagnostic is enabled (cached from the env). */
147159
public static bool $guardNewWorld = false;
148160

@@ -267,9 +279,14 @@ public function processNodes(
267279

268280
$expressionResultStorage = new ExpressionResultStorage();
269281
$scope->pushExpressionResultStorage($expressionResultStorage);
282+
// a fresh walk an extension starts mid-analysis must not feed the
283+
// interrupted walk's gatherer frames (see processStmtNodes())
284+
$gatherers = $this->nodeGatherers;
285+
$this->nodeGatherers = [];
270286
try {
271287
$this->processNodesWithStorage($nodes, $scope, $expressionResultStorage, $nodeCallback);
272288
} finally {
289+
$this->nodeGatherers = $gatherers;
273290
$scope->popExpressionResultStorage();
274291
}
275292
}
@@ -553,6 +570,11 @@ public function processStmtNodes(
553570
$scope = $scope->toWalkScope();
554571
$storage = new ExpressionResultStorage();
555572
$scope->pushExpressionResultStorage($storage);
573+
// a fresh walk an extension starts mid-analysis must not feed the
574+
// interrupted walk's gatherer frames - they describe the body walk
575+
// that was interrupted, not the nested one
576+
$gatherers = $this->nodeGatherers;
577+
$this->nodeGatherers = [];
556578
try {
557579
return $this->processStmtNodesInternal(
558580
$parentNode,
@@ -563,6 +585,7 @@ public function processStmtNodes(
563585
$context,
564586
)->toPublic();
565587
} finally {
588+
$this->nodeGatherers = $gatherers;
566589
$scope->popExpressionResultStorage();
567590
}
568591
}
@@ -1347,11 +1370,40 @@ private function hasContextSensitiveConstruct(Node $node): bool
13471370
*
13481371
* @param callable(Node $node, Scope $scope): void $nodeCallback
13491372
*/
1373+
/**
1374+
* Opens an engine-feeding gatherer frame for the duration of a body walk.
1375+
* The caller closes it in a finally block via popNodeGatherer().
1376+
*
1377+
* @param callable(Node, Scope): void $gatherer
1378+
*/
1379+
public function pushNodeGatherer(callable $gatherer): void
1380+
{
1381+
$this->nodeGatherers[] = $gatherer;
1382+
}
1383+
1384+
public function popNodeGatherer(): void
1385+
{
1386+
array_pop($this->nodeGatherers);
1387+
}
1388+
1389+
/**
1390+
* @param callable(Node $node, Scope $scope): void $nodeCallback
1391+
*/
13501392
public function replayRecording(RecordingNodeCallback $recording, callable $nodeCallback, ExpressionResultStorage $storage, MutatingScope $scope): void
13511393
{
13521394
$scope->pushExpressionResultStorage($storage);
13531395
try {
1354-
$recording->replayThrough($nodeCallback);
1396+
foreach ($recording->getPairs() as [$node, $pairScope]) {
1397+
if (!$pairScope instanceof MutatingScope) {
1398+
throw new ShouldNotHappenException();
1399+
}
1400+
// gatherer frames observe replayed emissions exactly like live
1401+
// ones - with the raw walk scope
1402+
foreach ($this->nodeGatherers as $gatherer) {
1403+
$gatherer($node, $pairScope);
1404+
}
1405+
$nodeCallback($node, $pairScope->toNodeCallbackScope());
1406+
}
13551407
} finally {
13561408
$scope->popExpressionResultStorage();
13571409
}
@@ -1384,15 +1436,14 @@ public function callNodeCallback(
13841436
ExpressionResultStorage $storage,
13851437
): void
13861438
{
1387-
// Engine-feeding gatherers must observe the node at the emission
1439+
// Engine-feeding gatherer frames observe the node at the emission
13881440
// position - their arrays are read as soon as the enclosing body walk
13891441
// returns. Gatherers are engine code and never ask about types -
13901442
// handing them the raw scope skips a NodeCallbackScope construction per
13911443
// emission; the scopes they capture (return statements, impure points)
13921444
// answer later asks through the storage hub like any MutatingScope.
1393-
while ($nodeCallback instanceof GatheringNodeCallback) {
1394-
($nodeCallback->getGatherer())($node, $scope);
1395-
$nodeCallback = $nodeCallback->getInner();
1445+
foreach ($this->nodeGatherers as $gatherer) {
1446+
$gatherer($node, $scope);
13961447
}
13971448

13981449
if ($nodeCallback instanceof NoopNodeCallback) {
@@ -1518,7 +1569,7 @@ private function processClosureNodeInternal(
15181569
$gatheredYieldStatementsWithScope = [];
15191570
$closureImpurePoints = [];
15201571
$invalidateExpressions = [];
1521-
$closureStmtsCallback = new GatheringNodeCallback(static function (Node $node, Scope $scope) use (&$executionEnds, &$gatheredReturnStatements, &$gatheredReturnStatementsWithScope, &$gatheredYieldStatements, &$gatheredYieldStatementsWithScope, &$closureScope, &$closureImpurePoints, &$invalidateExpressions): void {
1572+
$closureStmtsGatherer = static function (Node $node, Scope $scope) use (&$executionEnds, &$gatheredReturnStatements, &$gatheredReturnStatementsWithScope, &$gatheredYieldStatements, &$gatheredYieldStatementsWithScope, &$closureScope, &$closureImpurePoints, &$invalidateExpressions): void {
15221573
if ($scope->getAnonymousFunctionReflection() !== $closureScope->getAnonymousFunctionReflection()) {
15231574
return;
15241575
}
@@ -1551,10 +1602,15 @@ private function processClosureNodeInternal(
15511602

15521603
$gatheredReturnStatements[] = new ReturnStatement($scope, $node);
15531604
$gatheredReturnStatementsWithScope[] = [$node, $scope];
1554-
}, $nodeCallback);
1605+
};
15551606

15561607
if (count($byRefUses) === 0) {
1557-
$statementResult = $this->processStmtNodesInternal($expr, $expr->stmts, $closureScope, $storage, $closureStmtsCallback, StatementContext::createTopLevel());
1608+
$this->pushNodeGatherer($closureStmtsGatherer);
1609+
try {
1610+
$statementResult = $this->processStmtNodesInternal($expr, $expr->stmts, $closureScope, $storage, $nodeCallback, StatementContext::createTopLevel());
1611+
} finally {
1612+
$this->popNodeGatherer();
1613+
}
15581614
$publicStatementResult = $statementResult->toPublic();
15591615
$closureReturnStatementsNodeScope = $this->refineClosureNodeScope($closureScope, $scope, $expr, $gatheredReturnStatementsWithScope, $gatheredYieldStatementsWithScope, $executionEnds, $statementResult->getThrowPoints(), array_merge($closureImpurePoints, $statementResult->getImpurePoints()), $invalidateExpressions, $storage);
15601616
$this->callNodeCallback($nodeCallback, new ClosureReturnStatementsNode(
@@ -1632,23 +1688,28 @@ private function processClosureNodeInternal(
16321688
}
16331689

16341690
$storage = $originalStorage;
1635-
if (
1636-
$replayBodyRecording !== null && $replayPassStorage !== null
1637-
&& $replayPassResult !== null && $replayEntryScope !== null
1638-
&& $closureScope->equals($replayEntryScope)
1639-
) {
1640-
// the final walk would repeat the recorded fixpoint pass exactly
1641-
// (same entry scope, deterministic walk) - adopt the pass's result
1642-
// and replay its emissions through the gathering callback instead.
1643-
// The pass's own entry scope takes over: the recorded pairs carry
1644-
// its anonymous-function reflection, which the gathering filter
1645-
// compares by identity (the state is equals-identical anyway).
1646-
$closureScope = $replayEntryScope;
1647-
$originalStorage->mergeResults($replayPassStorage);
1648-
$this->replayRecording($replayBodyRecording, $closureStmtsCallback, $originalStorage, $closureScope);
1649-
$statementResult = $replayPassResult;
1650-
} else {
1651-
$statementResult = $this->processStmtNodesInternal($expr, $expr->stmts, $closureScope, $storage, $closureStmtsCallback, StatementContext::createTopLevel());
1691+
$this->pushNodeGatherer($closureStmtsGatherer);
1692+
try {
1693+
if (
1694+
$replayBodyRecording !== null && $replayPassStorage !== null
1695+
&& $replayPassResult !== null && $replayEntryScope !== null
1696+
&& $closureScope->equals($replayEntryScope)
1697+
) {
1698+
// the final walk would repeat the recorded fixpoint pass exactly
1699+
// (same entry scope, deterministic walk) - adopt the pass's result
1700+
// and replay its emissions through the real callback instead.
1701+
// The pass's own entry scope takes over: the recorded pairs carry
1702+
// its anonymous-function reflection, which the gatherer's filter
1703+
// compares by identity (the state is equals-identical anyway).
1704+
$closureScope = $replayEntryScope;
1705+
$originalStorage->mergeResults($replayPassStorage);
1706+
$this->replayRecording($replayBodyRecording, $nodeCallback, $originalStorage, $closureScope);
1707+
$statementResult = $replayPassResult;
1708+
} else {
1709+
$statementResult = $this->processStmtNodesInternal($expr, $expr->stmts, $closureScope, $storage, $nodeCallback, StatementContext::createTopLevel());
1710+
}
1711+
} finally {
1712+
$this->popNodeGatherer();
16521713
}
16531714
$publicStatementResult = $statementResult->toPublic();
16541715
$closureReturnStatementsNodeScope = $this->refineClosureNodeScope($closureScope, $scope, $expr, $gatheredReturnStatementsWithScope, $gatheredYieldStatementsWithScope, $executionEnds, $statementResult->getThrowPoints(), array_merge($closureImpurePoints, $statementResult->getImpurePoints()), $invalidateExpressions, $storage);
@@ -1776,7 +1837,7 @@ public function processArrowFunctionNode(
17761837
// feeds ClosureTypeResolver::buildClosureTypeForArrowFunction().
17771838
$arrowFunctionImpurePoints = [];
17781839
$invalidateExpressions = [];
1779-
$arrowFunctionStmtsCallback = new GatheringNodeCallback(static function (Node $node, Scope $innerScope) use ($arrowFunctionScope, &$arrowFunctionImpurePoints, &$invalidateExpressions): void {
1840+
$arrowFunctionStmtsGatherer = static function (Node $node, Scope $innerScope) use ($arrowFunctionScope, &$arrowFunctionImpurePoints, &$invalidateExpressions): void {
17801841
if ($innerScope->getAnonymousFunctionReflection() !== $arrowFunctionScope->getAnonymousFunctionReflection()) {
17811842
return;
17821843
}
@@ -1798,9 +1859,14 @@ public function processArrowFunctionNode(
17981859
true,
17991860
);
18001861
$invalidateExpressions[] = new InvalidateExprNode($node->getPropertyFetch());
1801-
}, $nodeCallback);
1862+
};
18021863

1803-
$exprResult = $this->processExprNode($stmt, $expr->expr, $arrowFunctionScope, $storage, $arrowFunctionStmtsCallback, ExpressionContext::createTopLevel());
1864+
$this->pushNodeGatherer($arrowFunctionStmtsGatherer);
1865+
try {
1866+
$exprResult = $this->processExprNode($stmt, $expr->expr, $arrowFunctionScope, $storage, $nodeCallback, ExpressionContext::createTopLevel());
1867+
} finally {
1868+
$this->popNodeGatherer();
1869+
}
18041870

18051871
$closureTypeThrowPoints = array_map(static fn (InternalThrowPoint $throwPoint) => $throwPoint->toPublic(), $exprResult->getThrowPoints());
18061872
$closureTypeImpurePoints = array_merge($arrowFunctionImpurePoints, $exprResult->getImpurePoints());

‎src/Analyser/StmtHandler/ClassMethodHandler.php‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
use PhpParser\Node\Stmt\Return_;
1111
use PHPStan\Analyser\DeprecatedAttributeResolver;
1212
use PHPStan\Analyser\ExpressionResultStorage;
13-
use PHPStan\Analyser\GatheringNodeCallback;
1413
use PHPStan\Analyser\ImpurePoint;
1514
use PHPStan\Analyser\InternalStatementResult;
1615
use PHPStan\Analyser\MutatingScope;
@@ -178,7 +177,7 @@ public function processStmt(
178177
$bodyStorage = $storage->duplicate();
179178
$scope->pushExpressionResultStorage($bodyStorage);
180179
try {
181-
$statementResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $methodScope, $bodyStorage, new GatheringNodeCallback(static function (Node $node, Scope $scope) use ($nodeScopeResolver, $methodScope, &$gatheredReturnStatements, &$gatheredYieldStatements, &$executionEnds, &$methodImpurePoints): void {
180+
$nodeScopeResolver->pushNodeGatherer(static function (Node $node, Scope $scope) use ($nodeScopeResolver, $methodScope, &$gatheredReturnStatements, &$gatheredYieldStatements, &$executionEnds, &$methodImpurePoints): void {
182181
if ($scope->getFunction() !== $methodScope->getFunction()) {
183182
return;
184183
}
@@ -216,7 +215,12 @@ public function processStmt(
216215
}
217216

218217
$gatheredReturnStatements[] = new ReturnStatement($scope, $node);
219-
}, $nodeCallback), StatementContext::createTopLevel())->toPublic();
218+
});
219+
try {
220+
$statementResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $methodScope, $bodyStorage, $nodeCallback, StatementContext::createTopLevel())->toPublic();
221+
} finally {
222+
$nodeScopeResolver->popNodeGatherer();
223+
}
220224

221225
$methodReflection = $methodScope->getFunction();
222226
if (!$methodReflection instanceof PhpMethodFromParserNodeReflection) {

‎src/Analyser/StmtHandler/ExpressionHandler.php‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use PhpParser\Node\Stmt\Expression;
99
use PHPStan\Analyser\ExpressionContext;
1010
use PHPStan\Analyser\ExpressionResultStorage;
11-
use PHPStan\Analyser\GatheringNodeCallback;
1211
use PHPStan\Analyser\InternalStatementExitPoint;
1312
use PHPStan\Analyser\InternalStatementResult;
1413
use PHPStan\Analyser\MutatingScope;
@@ -53,7 +52,7 @@ public function processStmt(
5352
}
5453
$hasAssign = false;
5554
$currentScope = $scope;
56-
$result = $nodeScopeResolver->processExprNode($stmt, $stmt->expr, $scope, $storage, new GatheringNodeCallback(static function (Node $node, Scope $scope) use ($currentScope, &$hasAssign): void {
55+
$nodeScopeResolver->pushNodeGatherer(static function (Node $node, Scope $scope) use ($currentScope, &$hasAssign): void {
5756
if (
5857
!($node instanceof VariableAssignNode) && !($node instanceof PropertyAssignNode)
5958
|| $scope->getAnonymousFunctionReflection() !== $currentScope->getAnonymousFunctionReflection()
@@ -63,7 +62,13 @@ public function processStmt(
6362
}
6463

6564
$hasAssign = true;
66-
}, $nodeCallback), ExpressionContext::createTopLevel());
65+
});
66+
try {
67+
$result = $nodeScopeResolver->processExprNode($stmt, $stmt->expr, $scope, $storage, $nodeCallback, ExpressionContext::createTopLevel());
68+
} finally {
69+
$nodeScopeResolver->popNodeGatherer();
70+
}
71+
6772
$nodeScopeResolver->callNodeCallback($nodeCallback, $stmt, $stmtScope, $storage);
6873
$throwPoints = array_filter($result->getThrowPoints(), static fn ($throwPoint) => $throwPoint->isExplicit());
6974
if (

‎src/Analyser/StmtHandler/FunctionHandler.php‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use PhpParser\Node\Stmt\Return_;
1010
use PHPStan\Analyser\DeprecatedAttributeResolver;
1111
use PHPStan\Analyser\ExpressionResultStorage;
12-
use PHPStan\Analyser\GatheringNodeCallback;
1312
use PHPStan\Analyser\ImpurePoint;
1413
use PHPStan\Analyser\InternalStatementResult;
1514
use PHPStan\Analyser\MutatingScope;
@@ -106,7 +105,7 @@ public function processStmt(
106105
$bodyStorage = $storage->duplicate();
107106
$scope->pushExpressionResultStorage($bodyStorage);
108107
try {
109-
$statementResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $functionScope, $bodyStorage, new GatheringNodeCallback(static function (Node $node, Scope $scope) use ($functionScope, &$gatheredReturnStatements, &$gatheredYieldStatements, &$executionEnds, &$functionImpurePoints): void {
108+
$nodeScopeResolver->pushNodeGatherer(static function (Node $node, Scope $scope) use ($functionScope, &$gatheredReturnStatements, &$gatheredYieldStatements, &$executionEnds, &$functionImpurePoints): void {
110109
if ($scope->getFunction() !== $functionScope->getFunction()) {
111110
return;
112111
}
@@ -135,7 +134,12 @@ public function processStmt(
135134
}
136135

137136
$gatheredReturnStatements[] = new ReturnStatement($scope, $node);
138-
}, $nodeCallback), StatementContext::createTopLevel())->toPublic();
137+
});
138+
try {
139+
$statementResult = $nodeScopeResolver->processStmtNodesInternal($stmt, $stmt->stmts, $functionScope, $bodyStorage, $nodeCallback, StatementContext::createTopLevel())->toPublic();
140+
} finally {
141+
$nodeScopeResolver->popNodeGatherer();
142+
}
139143

140144
$nodeScopeResolver->callNodeCallback($nodeCallback, new FunctionReturnStatementsNode(
141145
$stmt,

0 commit comments

Comments
 (0)