Skip to content

Commit c8eed73

Browse files
committed
GNSR - proper stmts analysis
1 parent e44856d commit c8eed73

12 files changed

+256
-63
lines changed

src/Analyser/Generator/ExprHandler/ClosureHandler.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ public function processClosureNode(
244244
};
245245

246246
if (count($byRefUses) === 0) {
247-
$closureStatementResult = yield new StmtsAnalysisRequest($expr->stmts, $closureScope, StatementContext::createTopLevel(), $closureStmtsCallback);
247+
$closureStatementResult = yield new StmtsAnalysisRequest($expr, $expr->stmts, $closureScope, StatementContext::createTopLevel(), $closureStmtsCallback);
248248
} else {
249249
$count = 0;
250250
$closureResultScope = null;
@@ -254,10 +254,10 @@ public function processClosureNode(
254254
yield new RestoreStorageRequest($storage);
255255
$prevScope = $closureScope;
256256

257-
$intermediaryClosureScopeResult = yield new StmtsAnalysisRequest($expr->stmts, $closureScope, StatementContext::createTopLevel(), new NoopNodeCallback());
257+
$intermediaryClosureScopeResult = yield new StmtsAnalysisRequest($expr, $expr->stmts, $closureScope, StatementContext::createTopLevel(), new NoopNodeCallback());
258258
$intermediaryClosureScope = $intermediaryClosureScopeResult->scope;
259259
foreach ($intermediaryClosureScopeResult->exitPoints as $exitPoint) {
260-
$intermediaryClosureScope = $intermediaryClosureScope->mergeWith($exitPoint->getScope());
260+
$intermediaryClosureScope = $intermediaryClosureScope->mergeWith($exitPoint->scope);
261261
}
262262

263263
if ($expr->getAttribute(ImmediatelyInvokedClosureVisitor::ATTRIBUTE_NAME) === true) {
@@ -282,7 +282,7 @@ public function processClosureNode(
282282
}
283283

284284
yield new RestoreStorageRequest($storage);
285-
$closureStatementResult = yield new StmtsAnalysisRequest($expr->stmts, $closureScope, StatementContext::createTopLevel(), $closureStmtsCallback);
285+
$closureStatementResult = yield new StmtsAnalysisRequest($expr, $expr->stmts, $closureScope, StatementContext::createTopLevel(), $closureStmtsCallback);
286286
$closureScope = $scope->processClosureScope($closureResultScope, null, $byRefUses);
287287
}
288288

src/Analyser/Generator/GeneratorNodeScopeResolver.php

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PhpParser\Node\Stmt;
1010
use PHPStan\Analyser\ExpressionContext;
1111
use PHPStan\Analyser\Generator\NodeHandler\AttrGroupsHandler;
12+
use PHPStan\Analyser\Generator\NodeHandler\StmtsHandler;
1213
use PHPStan\Analyser\Scope;
1314
use PHPStan\Analyser\StatementContext;
1415
use PHPStan\DependencyInjection\Container;
@@ -114,15 +115,15 @@ private function processStmtNodes(
114115
GeneratorScope $scope,
115116
callable $nodeCallback,
116117
StatementContext $context,
117-
): StmtAnalysisResult
118+
): void
118119
{
119-
$gen = new IdentifiedGeneratorInStack($this->analyseStmts($stmts, $scope, $context, null), $stmts, null, null);
120+
$gen = new IdentifiedGeneratorInStack($this->analyseInitialStmts($stmts, $scope, $context, null), $stmts, null, null);
120121
$gen->generator->current();
121122

122123
$stack = [];
123124

124125
try {
125-
return $this->runTrampoline(
126+
$this->runTrampoline(
126127
$fibersStorage,
127128
$exprAnalysisResultStorage,
128129
$gen,
@@ -158,7 +159,7 @@ private function runTrampoline(
158159
IdentifiedGeneratorInStack &$gen,
159160
callable $nodeCallback,
160161
array &$stack,
161-
): StmtAnalysisResult
162+
): void
162163
{
163164
while (true) {
164165
$pendingFibersGen = $this->processPendingFibers($fibersStorage, $exprAnalysisResultStorage);
@@ -224,7 +225,7 @@ private function runTrampoline(
224225
} elseif ($yielded instanceof StmtsAnalysisRequest) {
225226
$stack[] = $gen;
226227
$gen = new IdentifiedGeneratorInStack(
227-
$this->analyseStmts($yielded->stmts, $yielded->scope, $yielded->context, $yielded->alternativeNodeCallback),
228+
$this->analyseStmts($yielded->parentNode, $yielded->stmts, $yielded->scope, $yielded->context, $yielded->alternativeNodeCallback),
228229
$yielded->stmts,
229230
$yielded->originFile,
230231
$yielded->originLine,
@@ -291,49 +292,43 @@ private function runTrampoline(
291292
continue 2;
292293
}
293294

294-
if (!$result instanceof StmtAnalysisResult) {
295-
throw new ShouldNotHappenException('Top node should be Stmt');
295+
if ($result !== null) {
296+
throw new ShouldNotHappenException(sprintf('Null result is expected from analyseInitialStmts, given %s', get_debug_type($result)));
296297
}
297-
return $result;
298+
return;
298299
}
299300

300301
$gen = array_pop($stack);
301302
$gen->generator->send($result);
302303
}
303304
}
304305

306+
/**
307+
* @param array<Stmt> $stmts
308+
* @param (callable(Node, Scope, callable(Node, Scope): void): void)|null $alternativeNodeCallback
309+
*
310+
* @return Generator<int, GeneratorTValueType, GeneratorTSendType, void>
311+
*/
312+
private function analyseInitialStmts(array $stmts, GeneratorScope $scope, StatementContext $context, ?callable $alternativeNodeCallback): Generator
313+
{
314+
$handler = $this->container->getByType(StmtsHandler::class);
315+
$gen = $handler->analyseInitialStmts($stmts, $scope, $context, $alternativeNodeCallback);
316+
yield from $gen;
317+
}
318+
305319
/**
306320
* @param array<Stmt> $stmts
307321
* @param (callable(Node, Scope, callable(Node, Scope): void): void)|null $alternativeNodeCallback
308322
*
309323
* @return Generator<int, GeneratorTValueType, GeneratorTSendType, StmtAnalysisResult>
310324
*/
311-
private function analyseStmts(array $stmts, GeneratorScope $scope, StatementContext $context, ?callable $alternativeNodeCallback): Generator
325+
private function analyseStmts(Node $parentNode, array $stmts, GeneratorScope $scope, StatementContext $context, ?callable $alternativeNodeCallback): Generator
312326
{
313-
$exitPoints = [];
314-
$throwPoints = [];
315-
$impurePoints = [];
316-
$alreadyTerminated = false;
317-
$hasYield = false;
318-
319-
foreach ($stmts as $stmt) {
320-
$result = yield new StmtAnalysisRequest($stmt, $scope, $context, $alternativeNodeCallback);
321-
$scope = $result->scope;
322-
$hasYield = $hasYield || $result->hasYield;
323-
$exitPoints = array_merge($exitPoints, $result->exitPoints);
324-
$throwPoints = array_merge($throwPoints, $result->throwPoints);
325-
$impurePoints = array_merge($impurePoints, $result->impurePoints);
326-
$alreadyTerminated = $alreadyTerminated || $result->isAlwaysTerminating;
327-
}
327+
$handler = $this->container->getByType(StmtsHandler::class);
328+
$gen = $handler->analyseStmts($parentNode, $stmts, $scope, $context, $alternativeNodeCallback);
329+
yield from $gen;
328330

329-
return new StmtAnalysisResult(
330-
$scope,
331-
hasYield: $hasYield,
332-
isAlwaysTerminating: $alreadyTerminated,
333-
exitPoints: $exitPoints,
334-
throwPoints: $throwPoints,
335-
impurePoints: $impurePoints,
336-
);
331+
return $gen->getReturn();
337332
}
338333

339334
/**

src/Analyser/Generator/InternalEndStatementResult.php

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ final class InternalEndStatementResult
99
{
1010

1111
public function __construct(
12-
private Stmt $statement,
13-
private StmtAnalysisResult $result,
12+
public readonly Stmt $statement,
13+
public readonly StmtAnalysisResult $result,
1414
)
1515
{
1616
}
@@ -20,14 +20,4 @@ public function toPublic(): EndStatementResult
2020
return new EndStatementResult($this->statement, $this->result->toPublic());
2121
}
2222

23-
public function getStatement(): Stmt
24-
{
25-
return $this->statement;
26-
}
27-
28-
public function getResult(): StmtAnalysisResult
29-
{
30-
return $this->result;
31-
}
32-
3323
}

src/Analyser/Generator/InternalStatementExitPoint.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
final class InternalStatementExitPoint
99
{
1010

11-
public function __construct(private Stmt $statement, private GeneratorScope $scope)
11+
public function __construct(public readonly Stmt $statement, public readonly GeneratorScope $scope)
1212
{
1313
}
1414

@@ -17,14 +17,4 @@ public function toPublic(): StatementExitPoint
1717
return new StatementExitPoint($this->statement, $this->scope);
1818
}
1919

20-
public function getStatement(): Stmt
21-
{
22-
return $this->statement;
23-
}
24-
25-
public function getScope(): GeneratorScope
26-
{
27-
return $this->scope;
28-
}
29-
3020
}

src/Analyser/Generator/NodeHandler/PropertyHooksHandler.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use PHPStan\Node\InPropertyHookNode;
2424
use PHPStan\Node\PropertyAssignNode;
2525
use PHPStan\Node\PropertyHookReturnStatementsNode;
26+
use PHPStan\Node\PropertyHookStatementNode;
2627
use PHPStan\Node\ReturnStatement;
2728
use PHPStan\Parser\LineAttributesVisitor;
2829
use PHPStan\Reflection\Php\PhpMethodFromParserNodeReflection;
@@ -125,7 +126,7 @@ public function processPropertyHooks(
125126
$gatheredReturnStatements = [];
126127
$executionEnds = [];
127128
$methodImpurePoints = [];
128-
$statementResult = (yield new StmtsAnalysisRequest($stmts, $hookScope, StatementContext::createTopLevel(), static function (Node $node, Scope $scope, $nodeCallback) use ($hookScope, &$gatheredReturnStatements, &$executionEnds, &$hookImpurePoints): void {
129+
$statementResult = (yield new StmtsAnalysisRequest(new PropertyHookStatementNode($hook), $stmts, $hookScope, StatementContext::createTopLevel(), static function (Node $node, Scope $scope, $nodeCallback) use ($hookScope, &$gatheredReturnStatements, &$executionEnds, &$hookImpurePoints): void {
129130
$nodeCallback($node, $scope);
130131
if ($scope->getFunction() !== $hookScope->getFunction()) {
131132
return;

0 commit comments

Comments
 (0)