1
1
#!/usr/bin/env php
2
2
<?php declare (strict_types = 1 );
3
3
4
+ use PhpParser \Comment ;
4
5
use PhpParser \Node ;
5
6
use PhpParser \ParserFactory ;
7
+ use PHPStan \PhpDocParser \Ast \PhpDoc \PhpDocNode ;
8
+ use PHPStan \PhpDocParser \Ast \PhpDoc \PhpDocTagNode ;
9
+ use PHPStan \PhpDocParser \Ast \PhpDoc \ReturnTagValueNode ;
10
+ use PHPStan \PhpDocParser \Lexer \Lexer ;
11
+ use PHPStan \PhpDocParser \Parser \PhpDocParser ;
12
+ use PHPStan \PhpDocParser \Parser \TokenIterator ;
13
+ use PHPStan \PhpDocParser \Parser \ConstExprParser ;
14
+ use PHPStan \PhpDocParser \Parser \TypeParser ;
6
15
use Symfony \Component \Console \Application ;
7
16
use Symfony \Component \Console \Command \Command ;
8
17
use Symfony \Component \Console \Input \InputArgument ;
26
35
/** @var \PhpParser\PrettyPrinter\Standard */
27
36
private $ printer ;
28
37
38
+ /** @var Lexer */
39
+ private $ phpDocLexer ;
40
+
41
+ /** @var PhpDocParser */
42
+ private $ phpDocParser ;
43
+
29
44
public function __construct (
30
45
\PhpParser \Parser $ parser ,
31
46
\PhpParser \PrettyPrinter \Standard $ printer
@@ -34,6 +49,11 @@ public function __construct(
34
49
parent ::__construct ();
35
50
$ this ->parser = $ parser ;
36
51
$ this ->printer = $ printer ;
52
+ $ this ->phpDocLexer = new Lexer ();
53
+
54
+ $ constExprParser = new ConstExprParser ();
55
+ $ typeParser = new TypeParser ($ constExprParser );
56
+ $ this ->phpDocParser = new PhpDocParser ($ typeParser , $ constExprParser );
37
57
}
38
58
39
59
protected function configure (): void
@@ -363,6 +383,20 @@ private function compareFunctions(Node\FunctionLike $old, Node\FunctionLike $new
363
383
if ($ old ->getReturnType () === null && $ new ->getReturnType () !== null ) {
364
384
if ($ new ->getDocComment () !== null && strpos ($ new ->getDocComment ()->getText (), '@tentative-return-type ' ) !== 0 ) {
365
385
$ new ->returnType = null ; // @phpstan-ignore-line
386
+ if ($ old ->getDocComment () !== null ) {
387
+ $ oldPhpDocNode = $ this ->parseDocComment ($ old ->getDocComment ()->getText ());
388
+ $ oldPhpDocReturn = $ this ->findPhpDocReturn ($ oldPhpDocNode );
389
+ if ($ oldPhpDocNode !== null ) {
390
+ $ newPhpDocNode = $ this ->parseDocComment ($ new ->getDocComment ()->getText ());
391
+ $ newPhpDocReturn = $ this ->findPhpDocReturn ($ newPhpDocNode );
392
+ if ($ newPhpDocReturn === null ) {
393
+ $ children = $ newPhpDocNode ->children ;
394
+ $ children [] = new PhpDocTagNode ('@return ' , $ oldPhpDocReturn );
395
+ $ newPhpDocNodeWithReturn = new PhpDocNode ($ children );
396
+ $ new ->setDocComment (new Comment \Doc ((string ) $ newPhpDocNodeWithReturn ));
397
+ }
398
+ }
399
+ }
366
400
}
367
401
}
368
402
if ($ this ->printType ($ old ->getReturnType ()) !== $ this ->printType ($ new ->getReturnType ())) {
@@ -521,6 +555,25 @@ private function filterClassPhpDocs(Node\Stmt\ClassLike $class): Node\Stmt\Class
521
555
return $ class ;
522
556
}
523
557
558
+ private function parseDocComment (string $ docComment ): PhpDocNode
559
+ {
560
+ $ tokens = new TokenIterator ($ this ->phpDocLexer ->tokenize ($ docComment ));
561
+ $ phpDocNode = $ this ->phpDocParser ->parse ($ tokens );
562
+ $ tokens ->consumeTokenType (Lexer::TOKEN_END );
563
+
564
+ return $ phpDocNode ;
565
+ }
566
+
567
+ private function findPhpDocReturn (PhpDocNode $ node ): ?ReturnTagValueNode
568
+ {
569
+ $ returnTags = $ node ->getReturnTagValues ();
570
+ if (count ($ returnTags ) !== 1 ) {
571
+ return null ;
572
+ }
573
+
574
+ return $ returnTags [0 ];
575
+ }
576
+
524
577
/**
525
578
* @param array<string, string> $classes
526
579
* @param array<string, string> $functions
0 commit comments