Skip to content

Commit 584f343

Browse files
committed
skip non-closure sets in NoServiceSameNameSetClassRule
1 parent 6de587c commit 584f343

File tree

5 files changed

+91
-27
lines changed

5 files changed

+91
-27
lines changed

src/Rules/Symfony/ConfigClosure/NoServiceSameNameSetClassRule.php

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@
77
use Nette\Utils\Strings;
88
use PhpParser\Node;
99
use PhpParser\Node\Expr\ClassConstFetch;
10+
use PhpParser\Node\Expr\Closure;
1011
use PhpParser\Node\Expr\MethodCall;
12+
use PhpParser\NodeFinder;
1113
use PHPStan\Analyser\Scope;
1214
use PHPStan\Rules\Rule;
1315
use PHPStan\Rules\RuleError;
1416
use PHPStan\Rules\RuleErrorBuilder;
1517
use Symplify\PHPStanRules\Enum\RuleIdentifier\SymfonyRuleIdentifier;
1618
use Symplify\PHPStanRules\Helper\NamingHelper;
19+
use Symplify\PHPStanRules\Symfony\NodeAnalyzer\SymfonyClosureDetector;
1720

1821
/**
19-
* @implements Rule<MethodCall>
22+
* @implements Rule<Closure>
2023
*
2124
* @see \Symplify\PHPStanRules\Tests\Rules\Symfony\ConfigClosure\NoServiceSameNameSetClassRule\NoServiceSameNameSetClassRuleTest
2225
*/
@@ -27,62 +30,93 @@ final class NoServiceSameNameSetClassRule implements Rule
2730
*/
2831
public const ERROR_MESSAGE = 'No need to duplicate service class and name. Use only "$services->set(%s::class)" instead';
2932

33+
private NodeFinder $nodeFinder;
34+
35+
public function __construct()
36+
{
37+
$this->nodeFinder = new NodeFinder();
38+
}
39+
3040
public function getNodeType(): string
3141
{
32-
return MethodCall::class;
42+
return Closure::class;
3343
}
3444

3545
/**
36-
* @param MethodCall $node
46+
* @param Closure $node
3747
* @return RuleError[]
3848
*/
3949
public function processNode(Node $node, Scope $scope): array
4050
{
41-
if ($node->isFirstClassCallable()) {
51+
if (! SymfonyClosureDetector::detect($node)) {
4252
return [];
4353
}
4454

45-
if (! NamingHelper::isName($node->name, 'set')) {
46-
return [];
47-
}
55+
/** @var MethodCall[] $methodCalls */
56+
$methodCalls = $this->nodeFinder->findInstanceOf($node, MethodCall::class);
4857

49-
if (count($node->getArgs()) !== 2) {
50-
return [];
58+
$ruleErrors = [];
59+
60+
foreach ($methodCalls as $methodCall) {
61+
if ($methodCall->isFirstClassCallable()) {
62+
continue;
63+
}
64+
65+
if (! NamingHelper::isName($methodCall->name, 'set')) {
66+
continue;
67+
}
68+
69+
if (count($methodCall->getArgs()) !== 2) {
70+
continue;
71+
}
72+
73+
$serviceNameValue = $this->matchTwoArgsOfSameClassConstName($methodCall);
74+
if (! is_string($serviceNameValue)) {
75+
continue;
76+
}
77+
78+
if (str_contains($serviceNameValue, '\\')) {
79+
$serviceNameValue = Strings::after($serviceNameValue, '\\', -1);
80+
}
81+
82+
$identifierRuleError = RuleErrorBuilder::message(sprintf(self::ERROR_MESSAGE, $serviceNameValue))
83+
->identifier(SymfonyRuleIdentifier::NO_SERVICE_SAME_NAME_SET_CLASS)
84+
->line($methodCall->getStartLine())
85+
->build();
86+
87+
$ruleErrors[] = $identifierRuleError;
5188
}
5289

53-
$serviceName = $node->getArgs()[0]->value;
54-
$serviceType = $node->getArgs()[1]->value;
90+
return $ruleErrors;
91+
}
92+
93+
private function matchTwoArgsOfSameClassConstName(MethodCall $methodCall): ?string
94+
{
95+
$serviceName = $methodCall->getArgs()[0]->value;
96+
$serviceType = $methodCall->getArgs()[1]->value;
5597

5698
if (! $serviceName instanceof ClassConstFetch) {
57-
return [];
99+
return null;
58100
}
59101

60102
if (! $serviceType instanceof ClassConstFetch) {
61-
return [];
103+
return null;
62104
}
63105

64106
$serviceNameValue = NamingHelper::getName($serviceName->class);
65107
if (! is_string($serviceNameValue)) {
66-
return [];
108+
return null;
67109
}
68110

69111
$serviceTypeValue = NamingHelper::getName($serviceType->class);
70112
if (! is_string($serviceTypeValue)) {
71-
return [];
113+
return null;
72114
}
73115

74116
if ($serviceNameValue !== $serviceTypeValue) {
75-
return [];
117+
return null;
76118
}
77119

78-
if (str_contains($serviceNameValue, '\\')) {
79-
$serviceNameValue = Strings::after($serviceNameValue, '\\', -1);
80-
}
81-
82-
$identifierRuleError = RuleErrorBuilder::message(sprintf(self::ERROR_MESSAGE, $serviceNameValue))
83-
->identifier(SymfonyRuleIdentifier::NO_SERVICE_SAME_NAME_SET_CLASS)
84-
->build();
85-
86-
return [$identifierRuleError];
120+
return $serviceNameValue;
87121
}
88122
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Symplify\PHPStanRules\Tests\Rules\Symfony\ConfigClosure\NoServiceSameNameSetClassRule\Fixture;
4+
5+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
6+
use Symplify\PHPStanRules\Tests\Rules\Symfony\ConfigClosure\NoServiceSameNameSetClassRule\Source\SomeSetService;
7+
8+
class SkipNonClosureConstantSet
9+
{
10+
private const NAME = 'name';
11+
12+
private const TYPE = 'type';
13+
14+
public function run()
15+
{
16+
$this->set(self::NAME, self::TYPE);
17+
}
18+
}

tests/Rules/Symfony/ConfigClosure/NoServiceSameNameSetClassRule/Fixture/SomeConfigWithInvalidSet.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Symplify\PHPStanRules\Tests\Rules\Symfony\ConfigClosure\ServicesExcludedDirectoryMustExistRule\Fixture;
3+
namespace Symplify\PHPStanRules\Tests\Rules\Symfony\ConfigClosure\NoServiceSameNameSetClassRule\Fixture;
44

55
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
66
use Symplify\PHPStanRules\Tests\Rules\Symfony\ConfigClosure\NoServiceSameNameSetClassRule\Source\SomeSetService;

tests/Rules/Symfony/ConfigClosure/NoServiceSameNameSetClassRule/NoServiceSameNameSetClassRuleTest.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,20 @@ public static function provideData(): Iterator
2929
11,
3030
],
3131
]];
32+
33+
yield [__DIR__ . '/Fixture/SkipNonClosureConstantSet.php', []];
34+
}
35+
36+
/**
37+
* @return string[]
38+
*/
39+
public static function getAdditionalConfigFiles(): array
40+
{
41+
return [__DIR__ . '/config/configured_rule.neon'];
3242
}
3343

3444
protected function getRule(): Rule
3545
{
36-
return new NoServiceSameNameSetClassRule();
46+
return self::getContainer()->getByType(NoServiceSameNameSetClassRule::class);
3747
}
3848
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rules:
2+
- Symplify\PHPStanRules\Rules\Symfony\ConfigClosure\NoServiceSameNameSetClassRule

0 commit comments

Comments
 (0)