Skip to content

Commit 8c439cf

Browse files
[TypeDeclaration] Do not remove multiple docblocks with comment on TypedPropertyFromAssignsRector (rectorphp#3263)
* [TypeDeclaration] Do not remove multiple comments on TypedPropertyFromAssignsRector more fixture rules-tests Fix Fixed 🎉 final touch: eol Final touch: return null on multiple docs updated to mark Doc as Comment when not in last, as the used one is last Final touch: clean up [ci-review] Rector Rectify apply setDocComment after set comments attribute failing fixture preserve multiple docs no change type create another attribute to save previous docs * Try using new main doc * phpstan * [ci-review] Rector Rectify * [ci-review] Rector Rectify * Final touch: move to DocblockUpdater * Final touch: clean up * Final touch: clean up * Really Really Final touch: add comment Co-authored-by: GitHub Action <[email protected]>
1 parent 27d5714 commit 8c439cf

File tree

6 files changed

+217
-1
lines changed

6 files changed

+217
-1
lines changed

packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfoFactory.php

+51-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Rector\BetterPhpDocParser\PhpDocInfo;
66

7+
use PhpParser\Comment;
78
use PhpParser\Comment\Doc;
89
use PhpParser\Node;
910
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
@@ -35,7 +36,7 @@ public function __construct(
3536
private readonly StaticTypeMapper $staticTypeMapper,
3637
private readonly AnnotationNaming $annotationNaming,
3738
private readonly RectorChangeCollector $rectorChangeCollector,
38-
private readonly PhpDocNodeByTypeFinder $phpDocNodeByTypeFinder,
39+
private readonly PhpDocNodeByTypeFinder $phpDocNodeByTypeFinder
3940
) {
4041
}
4142

@@ -76,6 +77,13 @@ public function createFromNode(Node $node): ?PhpDocInfo
7677
$tokenIterator = new BetterTokenIterator([]);
7778
$phpDocNode = new PhpDocNode([]);
7879
} else {
80+
$comments = $node->getComments();
81+
$docs = array_filter($comments, static fn (Comment $comment): bool => $comment instanceof Doc);
82+
83+
if (count($docs) > 1) {
84+
$this->storePreviousDocs($node, $comments, $docComment);
85+
}
86+
7987
$text = $docComment->getText();
8088
$tokens = $this->lexer->tokenize($text);
8189
$tokenIterator = new BetterTokenIterator($tokens);
@@ -107,6 +115,48 @@ public function createEmpty(Node $node): PhpDocInfo
107115
return $phpDocInfo;
108116
}
109117

118+
/**
119+
* @param Comment[]|Doc[] $comments
120+
*/
121+
private function storePreviousDocs(Node $node, array $comments, Doc $doc): void
122+
{
123+
$previousDocsAsComments = [];
124+
$newMainDoc = null;
125+
126+
foreach ($comments as $comment) {
127+
// On last Doc, stop
128+
if ($comment === $doc) {
129+
break;
130+
}
131+
132+
// pure comment
133+
if (! $comment instanceof Doc) {
134+
$previousDocsAsComments[] = $comment;
135+
continue;
136+
}
137+
138+
// make Doc as comment Doc that not last
139+
$previousDocsAsComments[] = new Comment(
140+
$comment->getText(),
141+
$comment->getStartLine(),
142+
$comment->getStartFilePos(),
143+
$comment->getStartTokenPos(),
144+
$comment->getEndLine(),
145+
$comment->getEndFilePos(),
146+
$comment->getEndTokenPos()
147+
);
148+
149+
/**
150+
* Make last Doc before main Doc to candidate main Doc
151+
* so it can immediatelly be used as replacement of Main doc when main doc removed
152+
*/
153+
$newMainDoc = $comment;
154+
}
155+
156+
$node->setAttribute(AttributeKey::PREVIOUS_DOCS_AS_COMMENTS, $previousDocsAsComments);
157+
$node->setAttribute(AttributeKey::NEW_MAIN_DOC, $newMainDoc);
158+
}
159+
110160
/**
111161
* Needed for printing
112162
*/

packages/Comments/NodeDocBlock/DocBlockUpdater.php

+12
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ public function updateRefactoredNodeWithPhpDocInfo(Node $node): void
5858

5959
private function setCommentsAttribute(Node $node): void
6060
{
61+
if ($node->hasAttribute(AttributeKey::PREVIOUS_DOCS_AS_COMMENTS)) {
62+
/** @var Comment[] $previousDocsAsComments */
63+
$previousDocsAsComments = $node->getAttribute(AttributeKey::PREVIOUS_DOCS_AS_COMMENTS);
64+
$node->setAttribute(AttributeKey::COMMENTS, $previousDocsAsComments);
65+
}
66+
67+
if ($node->hasAttribute(AttributeKey::NEW_MAIN_DOC)) {
68+
/** @var Doc $newMainDoc */
69+
$newMainDoc = $node->getAttribute(AttributeKey::NEW_MAIN_DOC);
70+
$node->setDocComment($newMainDoc);
71+
}
72+
6173
$comments = array_filter($node->getComments(), static fn (Comment $comment): bool => ! $comment instanceof Doc);
6274
$node->setAttribute(AttributeKey::COMMENTS, $comments);
6375
}

packages/NodeTypeResolver/Node/AttributeKey.php

+12
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ final class AttributeKey
4141
*/
4242
public const COMMENTS = 'comments';
4343

44+
/**
45+
* Cover multi docs
46+
* @var string
47+
*/
48+
public const PREVIOUS_DOCS_AS_COMMENTS = 'previous_docs_as_comments';
49+
50+
/**
51+
* Cover multi docs
52+
* @var string
53+
*/
54+
public const NEW_MAIN_DOC = 'new_main_doc';
55+
4456
/**
4557
* Internal php-parser name.
4658
* Do not change this even if you want!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;
4+
5+
final class DoNotRemoveMultipleDocblockComment
6+
{
7+
// A comment
8+
9+
/**
10+
* Another comment
11+
*/
12+
13+
/**
14+
* @var \DateTime
15+
*/
16+
private $property;
17+
18+
public function __construct()
19+
{
20+
$this->property = new \DateTime();
21+
}
22+
}
23+
24+
?>
25+
-----
26+
<?php
27+
28+
namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;
29+
30+
final class DoNotRemoveMultipleDocblockComment
31+
{
32+
// A comment
33+
/**
34+
* Another comment
35+
*/
36+
private \DateTime $property;
37+
38+
public function __construct()
39+
{
40+
$this->property = new \DateTime();
41+
}
42+
}
43+
44+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;
4+
5+
final class DoNotRemoveMultipleDocblockComment2
6+
{
7+
// A comment
8+
9+
/**
10+
* Another comment
11+
*/
12+
13+
/**
14+
* Another comment inside main doc
15+
*
16+
* @var \DateTime
17+
*/
18+
private $property;
19+
20+
public function __construct()
21+
{
22+
$this->property = new \DateTime();
23+
}
24+
}
25+
26+
?>
27+
-----
28+
<?php
29+
30+
namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;
31+
32+
final class DoNotRemoveMultipleDocblockComment2
33+
{
34+
// A comment
35+
/**
36+
* Another comment
37+
*/
38+
/**
39+
* Another comment inside main doc
40+
*/
41+
private \DateTime $property;
42+
43+
public function __construct()
44+
{
45+
$this->property = new \DateTime();
46+
}
47+
}
48+
49+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;
4+
5+
final class PreserveMultipleDocsNoChange
6+
{
7+
// A comment
8+
9+
/**
10+
* Another comment
11+
*/
12+
13+
/**
14+
* @var class-string
15+
*/
16+
private $property;
17+
18+
public function __construct()
19+
{
20+
$this->property = 'DateTime';
21+
}
22+
}
23+
24+
?>
25+
-----
26+
<?php
27+
28+
namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;
29+
30+
final class PreserveMultipleDocsNoChange
31+
{
32+
// A comment
33+
34+
/**
35+
* Another comment
36+
*/
37+
38+
/**
39+
* @var class-string
40+
*/
41+
private string $property;
42+
43+
public function __construct()
44+
{
45+
$this->property = 'DateTime';
46+
}
47+
}
48+
49+
?>

0 commit comments

Comments
 (0)