@@ -385,25 +385,70 @@ public function getAllFuncInfos(): iterable {
385
385
}
386
386
}
387
387
388
+ class DocCommentTag {
389
+ /** @var string */
390
+ public $ name ;
391
+ /** @var ?string */
392
+ public $ value ;
393
+
394
+ public function __construct (string $ name , ?string $ value ) {
395
+ $ this ->name = $ name ;
396
+ $ this ->value = $ value ;
397
+ }
398
+
399
+ public function getValue (): string {
400
+ if ($ this ->value === null ) {
401
+ throw new Exception ("@ $ this ->name does not have a value " );
402
+ }
403
+
404
+ return $ this ->value ;
405
+ }
406
+
407
+ public function getVariableName (): string {
408
+ $ value = $ this ->getValue ();
409
+ if ($ value === null || strlen ($ value ) === 0 || $ value [0 ] !== '$ ' ) {
410
+ throw new Exception ("@ $ this ->name not followed by variable name " );
411
+ }
412
+
413
+ return substr ($ value , 1 );
414
+ }
415
+ }
416
+
417
+ /** @return DocCommentTag[] */
418
+ function parseDocComment (DocComment $ comment ): array {
419
+ $ commentText = substr ($ comment ->getText (), 2 , -2 );
420
+ $ tags = [];
421
+ foreach (explode ("\n" , $ commentText ) as $ commentLine ) {
422
+ $ regex = '/^\*\s*@([a-z-]+)(?:\s+(.+))$/ ' ;
423
+ if (preg_match ($ regex , trim ($ commentLine ), $ matches , PREG_UNMATCHED_AS_NULL )) {
424
+ $ tags [] = new DocCommentTag ($ matches [1 ], $ matches [2 ]);
425
+ }
426
+ }
427
+
428
+ return $ tags ;
429
+ }
430
+
388
431
function parseFunctionLike (
389
432
string $ name , ?string $ className , Node \FunctionLike $ func , ?string $ cond
390
433
): FuncInfo {
391
434
$ comment = $ func ->getDocComment ();
392
435
$ paramMeta = [];
393
436
$ alias = null ;
437
+ $ haveDocReturnType = false ;
394
438
395
439
if ($ comment ) {
396
- $ commentText = substr ($ comment ->getText (), 2 , -2 );
397
-
398
- foreach (explode ("\n" , $ commentText ) as $ commentLine ) {
399
- if (preg_match ('/^\*\s*@prefer-ref\s+\$(.+)$/ ' , trim ($ commentLine ), $ matches )) {
400
- $ varName = $ matches [1 ];
440
+ $ tags = parseDocComment ($ comment );
441
+ foreach ($ tags as $ tag ) {
442
+ if ($ tag ->name === 'prefer-ref ' ) {
443
+ $ varName = $ tag ->getVariableName ();
401
444
if (!isset ($ paramMeta [$ varName ])) {
402
445
$ paramMeta [$ varName ] = [];
403
446
}
404
447
$ paramMeta [$ varName ]['preferRef ' ] = true ;
405
- } else if (preg_match ('/^\*\s*@alias\s+(.+)$/ ' , trim ($ commentLine ), $ matches )) {
406
- $ alias = $ matches [1 ];
448
+ } else if ($ tag ->name === 'alias ' ) {
449
+ $ alias = $ tag ->getValue ();
450
+ } else if ($ tag ->name === 'return ' ) {
451
+ $ haveDocReturnType = true ;
407
452
}
408
453
}
409
454
}
@@ -455,6 +500,10 @@ function parseFunctionLike(
455
500
}
456
501
457
502
$ returnType = $ func ->getReturnType ();
503
+ if ($ returnType === null && !$ haveDocReturnType && substr ($ name , 0 , 2 ) !== '__ ' ) {
504
+ throw new Exception ("Missing return type for function $ name() " );
505
+ }
506
+
458
507
$ return = new ReturnInfo (
459
508
$ func ->returnsByRef (),
460
509
$ returnType ? Type::fromNode ($ returnType ) : null );
0 commit comments