@@ -34,7 +34,7 @@ public static function inject(Node\Stmt\Function_|Node\Stmt\ClassMethod $node):
3434 }
3535
3636 $ methodName = $ isClassMethod ? strtolower ($ node ->name ->toString ()) : '' ;
37- $ isMagicLifecycle = $ isClassMethod && \in_array ($ methodName , ['__construct ' , '__destruct ' , '__clone ' ], strict: true );
37+ $ isMagicLifecycle = $ isClassMethod && \in_array ($ methodName , ['__construct ' , '__destruct ' , '__clone ' ], true );
3838
3939 $ hasParam = $ isClassMethod || str_contains ($ docText , '@param ' ) || str_contains ($ docText , '@phpstan-param ' ) || str_contains ($ docText , '@psalm-param ' );
4040 $ hasReturn = ! $ isMagicLifecycle && ($ isClassMethod || str_contains ($ docText , '@return ' ) || str_contains ($ docText , '@phpstan-return ' ) || str_contains ($ docText , '@psalm-return ' ));
@@ -45,6 +45,7 @@ public static function inject(Node\Stmt\Function_|Node\Stmt\ClassMethod $node):
4545
4646 $ thisArg = self ::resolveThisArg ($ isClassMethod , $ node );
4747 $ isNativeVoid = $ node ->returnType instanceof Node \Identifier && strtolower ($ node ->returnType ->name ) === 'void ' ;
48+ $ needsReturnVars = str_contains ($ docText , ' is ' ) || (str_contains ($ docText , '@return ' ) && str_contains ($ docText , '$ ' ));
4849
4950 $ injectedStmts = [];
5051 if ($ hasParam ) {
@@ -54,10 +55,10 @@ public static function inject(Node\Stmt\Function_|Node\Stmt\ClassMethod $node):
5455 if ($ hasReturn ) {
5556 $ node ->stmts = self ::isGenerator ($ node )
5657 ? self ::wrapGeneratorReturns ($ node ->stmts , $ thisArg )
57- : self ::wrapNonGeneratorReturns ($ node ->stmts , $ thisArg , $ isNativeVoid );
58+ : self ::wrapNonGeneratorReturns ($ node ->stmts , $ thisArg , $ isNativeVoid, $ needsReturnVars );
5859 }
5960
60- $ node ->stmts = array_merge ( $ injectedStmts , $ node ->stmts ) ;
61+ $ node ->stmts = [... $ injectedStmts , ... $ node ->stmts ] ;
6162 }
6263
6364 private static function shouldSkipInjection (string $ docText ): bool
@@ -138,7 +139,7 @@ private static function buildParamInjections(
138139 str_contains ($ docText , 'iterable ' ) || str_contains ($ docText , 'Traversable ' ) || str_contains ($ docText , 'Generator ' ) || str_contains ($ docText , 'Iterator ' )
139140 );
140141
141- return array_merge ( $ injectedStmts , $ callableWrappers , $ iterableWrappers) ;
142+ return [... $ injectedStmts , ... $ callableWrappers , ... $ iterableWrappers] ;
142143 }
143144
144145 private static function buildSetupScopeStmt (Node \Expr $ thisArg ): Node \Stmt \If_
@@ -162,7 +163,7 @@ private static function buildSetupScopeStmt(Node\Expr $thisArg): Node\Stmt\If_
162163 ['stmts ' => [$ throwStmt ]]
163164 );
164165
165- $ ifStmt ->setAttribute ('typephp_injected ' , value: true );
166+ $ ifStmt ->setAttribute ('typephp_injected ' , true );
166167
167168 return $ ifStmt ;
168169 }
@@ -196,7 +197,7 @@ private static function buildParamWrappers(array $params, string $wrapperFunc, N
196197 )
197198 )
198199 );
199- $ expr ->setAttribute ('typephp_injected ' , value: true );
200+ $ expr ->setAttribute ('typephp_injected ' , true );
200201 $ wrappers [] = $ expr ;
201202 }
202203 }
@@ -228,15 +229,19 @@ public static function buildTypeErrorThrowStmt(Node\Expr $errorVar): Node\Stmt\E
228229 );
229230 }
230231
231- public static function buildReturnCheckCall (Node \Expr $ exprToWrap , Node \Expr $ thisArg ): Node \Expr \FuncCall
232+ public static function buildReturnCheckCall (Node \Expr $ exprToWrap , Node \Expr $ thisArg, bool $ needsReturnVars = false ): Node \Expr \FuncCall
232233 {
234+ $ varsArg = $ needsReturnVars
235+ ? new Node \Expr \FuncCall (new Node \Name ('get_defined_vars ' ))
236+ : new Node \Expr \Array_ ();
237+
233238 return new Node \Expr \FuncCall (
234239 new Node \Name ('\TypePHP\Internal\RuntimeTypeChecker::checkReturn ' ),
235240 [
236241 new Node \Arg (new Node \Scalar \MagicConst \Method ()),
237242 new Node \Arg ($ exprToWrap ),
238243 new Node \Arg ($ thisArg ),
239- new Node \Arg (new Node \ Expr \ FuncCall ( new Node \ Name ( ' get_defined_vars ' )) ),
244+ new Node \Arg ($ varsArg ),
240245 ]
241246 );
242247 }
@@ -253,10 +258,10 @@ public static function buildVoidReturnGuard(Node\Expr\FuncCall $checkCall): arra
253258 ),
254259 ['stmts ' => [self ::buildTypeErrorThrowStmt (new Node \Expr \Variable ('__typephpRet ' ))]]
255260 );
256- $ ifStmt ->setAttribute ('typephp_injected ' , value: true );
261+ $ ifStmt ->setAttribute ('typephp_injected ' , true );
257262
258263 $ retStmt = new Node \Stmt \Return_ (null );
259- $ retStmt ->setAttribute ('typephp_injected ' , value: true );
264+ $ retStmt ->setAttribute ('typephp_injected ' , true );
260265
261266 return [$ ifStmt , $ retStmt ];
262267 }
@@ -389,7 +394,7 @@ public function enterNode(Node $n): int|Node|null
389394 return null ;
390395 }
391396
392- $ n ->setAttribute ('typephp_wrapped ' , value: true );
397+ $ n ->setAttribute ('typephp_wrapped ' , true );
393398
394399 return FunctionContractInjector::buildWrappedYieldNode ($ n , $ this ->thisArg );
395400 }
@@ -399,7 +404,7 @@ public function enterNode(Node $n): int|Node|null
399404 return null ;
400405 }
401406
402- $ n ->setAttribute ('typephp_wrapped ' , value: true );
407+ $ n ->setAttribute ('typephp_wrapped ' , true );
403408
404409 $ n ->expr = new Node \Expr \FuncCall (
405410 new Node \Name ('\TypePHP\Internal\RuntimeTypeChecker::wrapIterable ' ),
@@ -427,13 +432,14 @@ public function enterNode(Node $n): int|Node|null
427432 *
428433 * @return array<Node\Stmt>
429434 */
430- private static function wrapNonGeneratorReturns (array $ stmts , Node \Expr $ thisArg , bool $ isNativeVoid ): array
435+ private static function wrapNonGeneratorReturns (array $ stmts , Node \Expr $ thisArg , bool $ isNativeVoid, bool $ needsReturnVars = false ): array
431436 {
432437 $ traverser = new NodeTraverser ();
433- $ traverser ->addVisitor (new class ($ thisArg , $ isNativeVoid ) extends NodeVisitorAbstract {
438+ $ traverser ->addVisitor (new class ($ thisArg , $ isNativeVoid, $ needsReturnVars ) extends NodeVisitorAbstract {
434439 public function __construct (
435440 private Node \Expr $ thisArg ,
436- private bool $ isNativeVoid
441+ private bool $ isNativeVoid ,
442+ private bool $ needsReturnVars
437443 ) {
438444 }
439445
@@ -445,7 +451,7 @@ public function enterNode(Node $n): int|array|null
445451
446452 if ($ n instanceof Node \Stmt \Return_) {
447453 $ exprToWrap = $ n ->expr ?? new Node \Expr \ConstFetch (new Node \Name ('null ' ));
448- $ checkCall = FunctionContractInjector::buildReturnCheckCall ($ exprToWrap , $ this ->thisArg );
454+ $ checkCall = FunctionContractInjector::buildReturnCheckCall ($ exprToWrap , $ this ->thisArg , $ this -> needsReturnVars );
449455
450456 if ($ this ->isNativeVoid ) {
451457 return FunctionContractInjector::buildVoidReturnGuard ($ checkCall );
@@ -463,13 +469,13 @@ public function enterNode(Node $n): int|array|null
463469
464470 $ lastStmt = end ($ newStmts );
465471 if (! $ lastStmt instanceof Node \Stmt \Return_ && ! ($ lastStmt instanceof Node \Stmt \Expression && $ lastStmt ->expr instanceof Node \Expr \Throw_)) {
466- $ checkCall = self ::buildReturnCheckCall (new Node \Expr \ConstFetch (new Node \Name ('null ' )), $ thisArg );
472+ $ checkCall = self ::buildReturnCheckCall (new Node \Expr \ConstFetch (new Node \Name ('null ' )), $ thisArg, $ needsReturnVars );
467473
468474 if ($ isNativeVoid ) {
469- $ newStmts = array_merge ( $ newStmts , self ::buildVoidReturnGuard ($ checkCall )) ;
475+ $ newStmts = [... $ newStmts , ... self ::buildVoidReturnGuard ($ checkCall )] ;
470476 } else {
471477 $ retStmt = new Node \Stmt \Return_ (self ::buildTernaryReturnExpr ($ checkCall ));
472- $ retStmt ->setAttribute ('typephp_injected ' , value: true );
478+ $ retStmt ->setAttribute ('typephp_injected ' , true );
473479 $ newStmts [] = $ retStmt ;
474480 }
475481 }
0 commit comments