Skip to content

Commit 9b6bb2f

Browse files
committed
AttributeNamedArgumentsRule made fixable
1 parent 3b32f1c commit 9b6bb2f

File tree

4 files changed

+110
-1
lines changed

4 files changed

+110
-1
lines changed

build/PHPStan/Build/AttributeNamedArgumentsRule.php

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
use PhpParser\Node;
66
use PhpParser\Node\Attribute;
77
use PHPStan\Analyser\Scope;
8+
use PHPStan\Reflection\ReflectionProvider;
89
use PHPStan\Rules\Rule;
910
use PHPStan\Rules\RuleErrorBuilder;
11+
use PHPStan\ShouldNotHappenException;
12+
use function count;
1013
use function sprintf;
1114

1215
/**
@@ -15,22 +18,60 @@
1518
final class AttributeNamedArgumentsRule implements Rule
1619
{
1720

21+
public function __construct(private ReflectionProvider $reflectionProvider)
22+
{
23+
}
24+
1825
public function getNodeType(): string
1926
{
2027
return Attribute::class;
2128
}
2229

2330
public function processNode(Node $node, Scope $scope): array
2431
{
32+
$attributeName = $node->name->toString();
33+
if (!$this->reflectionProvider->hasClass($attributeName)) {
34+
return [];
35+
}
36+
37+
$attributeReflection = $this->reflectionProvider->getClass($attributeName);
38+
if (!$attributeReflection->hasConstructor()) {
39+
return [];
40+
}
41+
$constructor = $attributeReflection->getConstructor();
42+
$variants = $constructor->getVariants();
43+
if (count($variants) !== 1) {
44+
return [];
45+
}
46+
47+
$parameters = $variants[0]->getParameters();
48+
2549
foreach ($node->args as $arg) {
2650
if ($arg->name !== null) {
27-
continue;
51+
break;
2852
}
2953

3054
return [
3155
RuleErrorBuilder::message(sprintf('Attribute %s is not using named arguments.', $node->name->toString()))
3256
->identifier('phpstan.attributeWithoutNamedArguments')
3357
->nonIgnorable()
58+
->fixNode($node, static function (Node $node) use ($parameters) {
59+
$args = $node->args;
60+
foreach ($args as $i => $arg) {
61+
if ($arg->name !== null) {
62+
break;
63+
}
64+
65+
$parameterName = $parameters[$i]->getName();
66+
if ($parameterName === '') {
67+
throw new ShouldNotHappenException();
68+
}
69+
70+
$arg->name = new Node\Identifier($parameterName);
71+
}
72+
73+
return $node;
74+
})
3475
->build(),
3576
];
3677
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Build;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<AttributeNamedArgumentsRule>
10+
*/
11+
class AttributeNamedArgumentsRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new AttributeNamedArgumentsRule($this->createReflectionProvider());
17+
}
18+
19+
public function testRule(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/attribute-arguments.php'], [
22+
[
23+
'Attribute PHPStan\DependencyInjection\AutowiredService is not using named arguments.',
24+
13,
25+
],
26+
]);
27+
}
28+
29+
public function testFix(): void
30+
{
31+
$this->fix(__DIR__ . '/data/attribute-arguments.php', __DIR__ . '/data/attribute-arguments.php.fixed');
32+
}
33+
34+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace AttributeArguments;
4+
5+
use PHPStan\DependencyInjection\AutowiredService;
6+
7+
#[AutowiredService(name: 'foo')]
8+
class Foo
9+
{
10+
11+
}
12+
13+
#[AutowiredService('foo')]
14+
class Bar
15+
{
16+
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace AttributeArguments;
4+
5+
use PHPStan\DependencyInjection\AutowiredService;
6+
7+
#[AutowiredService(name: 'foo')]
8+
class Foo
9+
{
10+
11+
}
12+
13+
#[AutowiredService(name: 'foo')]
14+
class Bar
15+
{
16+
17+
}

0 commit comments

Comments
 (0)