Skip to content

Commit de42b4f

Browse files
SanderMullerclaude
andauthored
Test when the fixer writes new class names (#6934)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent eafa5ab commit de42b4f

5 files changed

Lines changed: 155 additions & 6 deletions

File tree

‎tests/PHPStan/Fixable/AddNamesRule.php‎

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
use PHPStan\Rules\Rule;
1313
use PHPStan\Rules\RuleErrorBuilder;
1414
use function count;
15+
use function in_array;
1516
use function preg_match_all;
1617
use function sprintf;
1718

1819
/**
1920
* Turns each `@add-name \Some\Class` tag into a native parameter type
2021
* and a `@param` tag, to show new names are written the same way
21-
* in PHP code and in PHPDocs.
22+
* in PHP code and in PHPDocs. An `@add-doc-name \Some\Class` tag becomes
23+
* only a `@param` tag.
2224
*
2325
* @implements Rule<Node\Stmt\ClassMethod>
2426
*/
@@ -37,19 +39,27 @@ public function getNodeType(): string
3739
public function processNode(Node $node, Scope $scope): array
3840
{
3941
$docComment = $node->getDocComment();
40-
if ($docComment === null || preg_match_all('/@add-name\s+\\\\?(\S+)/', $docComment->getText(), $matches) === 0) {
42+
if ($docComment === null || preg_match_all('/@add-(doc-)?name\s+\\\\?(\S+)/', $docComment->getText(), $matches) === 0) {
4143
return [];
4244
}
4345

44-
$names = $matches[1];
46+
$names = $matches[2];
47+
$parameterNames = [];
48+
foreach ($matches[1] as $j => $docOnly) {
49+
if ($docOnly !== '') {
50+
continue;
51+
}
52+
53+
$parameterNames[] = $names[$j];
54+
}
4555

4656
return [
4757
RuleErrorBuilder::message(sprintf('Adding %d names.', count($names)))
4858
->identifier('tests.addNames')
49-
->fixNode($node, function (Node\Stmt\ClassMethod $method) use ($names) {
59+
->fixNode($node, function (Node\Stmt\ClassMethod $method) use ($parameterNames) {
5060
$i = 0;
5161
$this->phpDocEditor->edit($method, static function (\PHPStan\PhpDocParser\Ast\Node $phpDocNode) use (&$i) {
52-
if (!$phpDocNode instanceof PhpDocTagNode || $phpDocNode->name !== '@add-name') {
62+
if (!$phpDocNode instanceof PhpDocTagNode || !in_array($phpDocNode->name, ['@add-name', '@add-doc-name'], true)) {
5363
return null;
5464
}
5565

@@ -64,7 +74,7 @@ public function processNode(Node $node, Scope $scope): array
6474
));
6575
});
6676

67-
foreach ($names as $j => $name) {
77+
foreach ($parameterNames as $j => $name) {
6878
$method->params[] = new Node\Param(
6979
new Node\Expr\Variable('p' . ($j + 1)),
7080
type: new FullyQualified($name),

‎tests/PHPStan/Fixable/AddNamesRuleTest.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,9 @@ public function testFixSortedUses(): void
3737
$this->fix(__DIR__ . '/data/add-names-sorted-uses.php', __DIR__ . '/data/add-names-sorted-uses.php.fixed');
3838
}
3939

40+
public function testFixPhpDocOnly(): void
41+
{
42+
$this->fix(__DIR__ . '/data/add-names-phpdoc-only.php', __DIR__ . '/data/add-names-phpdoc-only.php.fixed');
43+
}
44+
4045
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Fixable;
4+
5+
use PhpParser\Comment\Doc;
6+
use PhpParser\Node;
7+
use PhpParser\Node\Name\FullyQualified;
8+
use PhpParser\Node\Stmt\ClassMethod;
9+
use PhpParser\NodeFinder;
10+
use PhpParser\NodeTraverser;
11+
use PhpParser\NodeVisitor\CloningVisitor;
12+
use PhpParser\ParserFactory;
13+
use PHPUnit\Framework\Attributes\DataProvider;
14+
use PHPUnit\Framework\TestCase;
15+
16+
class NewClassNamesVisitorTest extends TestCase
17+
{
18+
19+
private const CODE = <<<'PHP'
20+
<?php
21+
22+
namespace HasWork;
23+
24+
class Foo
25+
{
26+
27+
/** @return int */
28+
#[\Override]
29+
public function doFoo(\Bar\Baz $baz): int
30+
{
31+
return strlen('');
32+
}
33+
34+
}
35+
PHP;
36+
37+
/**
38+
* @return iterable<string, array{callable(ClassMethod): Node, bool}>
39+
*/
40+
public static function dataHasNewNames(): iterable
41+
{
42+
yield 'unchanged' => [
43+
static fn (ClassMethod $method): Node => $method,
44+
false,
45+
];
46+
47+
yield 'removed attribute' => [
48+
static function (ClassMethod $method): Node {
49+
$method->attrGroups = [];
50+
return $method;
51+
},
52+
false,
53+
];
54+
55+
yield 'added name' => [
56+
static function (ClassMethod $method): Node {
57+
$method->params[] = new Node\Param(new Node\Expr\Variable('qux'), type: new FullyQualified('Qux\Quux'));
58+
return $method;
59+
},
60+
true,
61+
];
62+
63+
yield 'changed PHPDoc' => [
64+
static function (ClassMethod $method): Node {
65+
$method->setDocComment(new Doc('/** @return positive-int */'));
66+
return $method;
67+
},
68+
true,
69+
];
70+
71+
yield 'new node with a PHPDoc' => [
72+
static function (ClassMethod $method): Node {
73+
$newMethod = new ClassMethod('doBar');
74+
$newMethod->setDocComment(new Doc('/** @return void */'));
75+
return $newMethod;
76+
},
77+
true,
78+
];
79+
}
80+
81+
/**
82+
* @param callable(ClassMethod): Node $fix
83+
*/
84+
#[DataProvider('dataHasNewNames')]
85+
public function testHasNewNames(callable $fix, bool $expected): void
86+
{
87+
$stmts = (new ParserFactory())->createForHostVersion()->parse(self::CODE);
88+
$this->assertNotNull($stmts);
89+
90+
$newStmts = (new NodeTraverser(new CloningVisitor()))->traverse($stmts);
91+
$method = (new NodeFinder())->findFirstInstanceOf($newStmts, ClassMethod::class);
92+
$this->assertNotNull($method);
93+
94+
$this->assertSame($expected, NewClassNamesVisitor::hasNewNames([$fix($method)]));
95+
}
96+
97+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace AddNamesPhpDocOnly;
4+
5+
use AddNamesImported\Foo;
6+
7+
class Bar
8+
{
9+
10+
/**
11+
* @add-doc-name \AddNamesImported\Foo
12+
* @add-doc-name \AddNamesOther\AddNamesUnique
13+
*/
14+
public function doFoo($p1, $p2): void
15+
{
16+
}
17+
18+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace AddNamesPhpDocOnly;
4+
5+
use AddNamesImported\Foo;
6+
use AddNamesOther\AddNamesUnique;
7+
8+
class Bar
9+
{
10+
11+
/**
12+
* @param Foo $p1
13+
* @param AddNamesUnique $p2
14+
*/
15+
public function doFoo($p1, $p2): void
16+
{
17+
}
18+
19+
}

0 commit comments

Comments
 (0)