1616use PHPStan \Node \Printer \ExprPrinter ;
1717use PHPStan \ShouldNotHappenException ;
1818use Throwable ;
19- use function array_map ;
2019use function array_merge ;
2120use function array_pop ;
2221use function count ;
@@ -162,26 +161,35 @@ private function runTrampoline(
162161 ): StmtAnalysisResult
163162 {
164163 while (true ) {
165- $ this ->processPendingFibers ($ fibersStorage , $ exprAnalysisResultStorage );
164+ $ pendingFibersGen = $ this ->processPendingFibers ($ fibersStorage , $ exprAnalysisResultStorage );
165+ if ($ pendingFibersGen ->valid ()) {
166+ $ stack [] = $ gen ;
167+ $ gen = new IdentifiedGeneratorInStack ($ pendingFibersGen , new Stmt \Expression (new Node \Scalar \String_ ('pendingFibers ' )), null , null );
168+ }
166169
167170 if ($ gen ->generator ->valid ()) {
168171 $ yielded = $ gen ->generator ->current ();
169172
170173 if ($ yielded instanceof NodeCallbackRequest) {
171174 $ alternativeNodeCallback = $ yielded ->alternativeNodeCallback ;
172- $ this ->invokeNodeCallback (
173- $ fibersStorage ,
174- $ exprAnalysisResultStorage ,
175+ $ stack [] = $ gen ;
176+ $ gen = new IdentifiedGeneratorInStack (
177+ $ this ->invokeNodeCallback (
178+ $ fibersStorage ,
179+ $ exprAnalysisResultStorage ,
180+ $ yielded ->node ,
181+ $ yielded ->scope ,
182+ $ alternativeNodeCallback !== null
183+ ? static function (Node $ node , Scope $ scope ) use ($ alternativeNodeCallback , $ nodeCallback ): void {
184+ $ alternativeNodeCallback ($ node , $ scope , $ nodeCallback );
185+ }
186+ : $ nodeCallback ,
187+ ),
175188 $ yielded ->node ,
176- $ yielded ->scope ,
177- $ alternativeNodeCallback !== null
178- ? static function (Node $ node , Scope $ scope ) use ($ alternativeNodeCallback , $ nodeCallback ): void {
179- $ alternativeNodeCallback ($ node , $ scope , $ nodeCallback );
180- }
181- : $ nodeCallback ,
189+ $ yielded ->originFile ,
190+ $ yielded ->originLine ,
182191 );
183-
184- $ gen ->generator ->next ();
192+ $ gen ->generator ->current ();
185193 continue ;
186194 } elseif ($ yielded instanceof ExprAnalysisRequest) {
187195 $ stack [] = $ gen ;
@@ -272,26 +280,21 @@ private function runTrampoline(
272280 throw new ShouldNotHappenException ('Pending fibers with an empty stack should be about synthetic nodes ' );
273281 }
274282
275- // todo problem with noop callback
276- $ this ->processStmtNodes (
277- $ fibersStorage ,
278- $ exprAnalysisResultStorage ,
279- [new Stmt \Expression ($ request ->expr )],
280- $ request ->scope ,
281- static function () {
282- },
283- StatementContext::createTopLevel (),
283+ $ stack [] = $ gen ;
284+ $ gen = new IdentifiedGeneratorInStack (
285+ $ this ->analyseExpr ($ exprAnalysisResultStorage , $ request ->stmt , $ request ->expr , $ request ->scope , $ request ->context , $ request ->alternativeNodeCallback ),
286+ $ request ->expr ,
287+ $ request ->originFile ,
288+ $ request ->originLine ,
284289 );
290+ $ gen ->generator ->current ();
291+ continue 2 ;
285292 }
286293
287- if (count ($ fibersStorage ->pendingFibers ) === 0 ) {
288- if (!$ result instanceof StmtAnalysisResult) {
289- throw new ShouldNotHappenException ('Top node should be Stmt ' );
290- }
291- return $ result ;
294+ if (!$ result instanceof StmtAnalysisResult) {
295+ throw new ShouldNotHappenException ('Top node should be Stmt ' );
292296 }
293-
294- throw new ShouldNotHappenException (sprintf ('Cannot finish analysis, pending fibers about: %s ' , implode (', ' , array_map (static fn (array $ fiber ) => get_class ($ fiber ['request ' ]->expr ), $ fibersStorage ->pendingFibers ))));
297+ return $ result ;
295298 }
296299
297300 $ gen = array_pop ($ stack );
@@ -391,8 +394,7 @@ private function runInFiber(callable $callback): Generator
391394
392395 while (!$ fiber ->isTerminated ()) {
393396 if ($ request instanceof ExprAnalysisRequest) {
394- $ result = yield $ request ;
395- $ request = $ fiber ->resume ($ result );
397+ $ request = $ fiber ->resume (yield $ request );
396398 continue ;
397399 }
398400
@@ -463,31 +465,35 @@ private function analyseExprForType(ExprAnalysisResultStorage $storage, Expr $ex
463465
464466 /**
465467 * @param callable(Node, Scope): void $nodeCallback
468+ * @return Generator<int, GeneratorTValueType, GeneratorTSendType, null>
466469 */
467470 private function invokeNodeCallback (
468471 PendingFibersStorage $ fibersStorage ,
469472 ExprAnalysisResultStorage $ exprAnalysisResultStorage ,
470473 Node $ node ,
471474 Scope $ scope ,
472475 callable $ nodeCallback ,
473- ): void
476+ ): Generator
474477 {
475478 $ fiber = new Fiber (static function () use ($ node , $ scope , $ nodeCallback ) {
476479 $ nodeCallback ($ node , $ scope );
477480 });
478481 $ request = $ fiber ->start ();
479- $ this ->runFiberForNodeCallback ($ fibersStorage , $ exprAnalysisResultStorage , $ fiber , $ request );
482+ yield from $ this ->runFiberForNodeCallback ($ fibersStorage , $ exprAnalysisResultStorage , $ fiber , $ request );
483+
484+ return null ;
480485 }
481486
482487 /**
483- * @param Fiber<mixed, ExprAnalysisResult, null, ExprAnalysisRequest> $fiber
488+ * @param Fiber<mixed, ExprAnalysisResult|null, null, ExprAnalysisRequest|NodeCallbackRequest> $fiber
489+ * @return Generator<int, GeneratorTValueType, GeneratorTSendType, void>
484490 */
485491 private function runFiberForNodeCallback (
486492 PendingFibersStorage $ fibersStorage ,
487493 ExprAnalysisResultStorage $ exprAnalysisResultStorage ,
488494 Fiber $ fiber ,
489- ? ExprAnalysisRequest $ request ,
490- ): void
495+ ExprAnalysisRequest | NodeCallbackRequest | null $ request ,
496+ ): Generator
491497 {
492498 while (!$ fiber ->isTerminated ()) {
493499 if ($ request instanceof ExprAnalysisRequest) {
@@ -506,6 +512,10 @@ private function runFiberForNodeCallback(
506512 ];
507513 return ;
508514 }
515+ if ($ request instanceof NodeCallbackRequest) {
516+ $ request = $ fiber ->resume (yield $ request );
517+ continue ;
518+ }
509519
510520 throw new ShouldNotHappenException (
511521 'Unknown fiber suspension: ' . get_debug_type ($ request ),
@@ -519,21 +529,34 @@ private function runFiberForNodeCallback(
519529 }
520530 }
521531
522- private function processPendingFibers (PendingFibersStorage $ fibersStorage , ExprAnalysisResultStorage $ exprAnalysisResultStorage ): void
532+ /**
533+ * @return Generator<int, GeneratorTValueType, GeneratorTSendType, void>
534+ */
535+ private function processPendingFibers (PendingFibersStorage $ fibersStorage , ExprAnalysisResultStorage $ exprAnalysisResultStorage ): Generator
523536 {
524- foreach ($ fibersStorage ->pendingFibers as $ key => $ pending ) {
525- $ request = $ pending ['request ' ];
526- $ exprAnalysisResult = $ exprAnalysisResultStorage ->findExprAnalysisResult ($ request ->expr );
537+ $ restartLoop = true ;
527538
528- if ($ exprAnalysisResult === null ) {
529- continue ;
530- }
539+ while ($ restartLoop ) {
540+ $ restartLoop = false ;
541+
542+ foreach ($ fibersStorage ->pendingFibers as $ key => $ pending ) {
543+ $ request = $ pending ['request ' ];
544+ $ exprAnalysisResult = $ exprAnalysisResultStorage ->findExprAnalysisResult ($ request ->expr );
531545
532- unset($ fibersStorage ->pendingFibers [$ key ]);
546+ if ($ exprAnalysisResult === null ) {
547+ continue ;
548+ }
533549
534- $ fiber = $ pending ['fiber ' ];
535- $ request = $ fiber ->resume ($ exprAnalysisResult );
536- $ this ->runFiberForNodeCallback ($ fibersStorage , $ exprAnalysisResultStorage , $ fiber , $ request );
550+ unset($ fibersStorage ->pendingFibers [$ key ]);
551+ $ restartLoop = true ;
552+
553+ $ fiber = $ pending ['fiber ' ];
554+ $ request = $ fiber ->resume ($ exprAnalysisResult );
555+ yield from $ this ->runFiberForNodeCallback ($ fibersStorage , $ exprAnalysisResultStorage , $ fiber , $ request );
556+
557+ // Break and restart the loop since the array may have been modified
558+ break ;
559+ }
537560 }
538561 }
539562
0 commit comments