Skip to content

Commit 63986b1

Browse files
committed
phsptan fixes
1 parent a53fae6 commit 63986b1

File tree

13 files changed

+237
-109
lines changed

13 files changed

+237
-109
lines changed

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,24 @@
88
"ext-xml": "*"
99
},
1010
"require-dev": {
11+
"phpecs/phpecs": "^2.0.1",
1112
"phpstan/extension-installer": "^1.4",
1213
"phpstan/phpstan": "^2.1.8",
1314
"phpstan/phpstan-webmozart-assert": "^2.0",
1415
"phpunit/phpunit": "^11.4",
1516
"rector/rector-src": "dev-main",
17+
"rector/type-perfect": "^2.0",
1618
"symfony/config": "^6.4",
1719
"symfony/dependency-injection": "^6.4",
1820
"symfony/http-kernel": "^6.4",
1921
"symfony/routing": "^6.4",
2022
"symfony/security-core": "^6.4",
2123
"symfony/security-http": "^6.4",
2224
"symfony/validator": "^6.4",
23-
"phpecs/phpecs": "^2.0.1",
2425
"symplify/vendor-patches": "^11.3",
2526
"tomasvotruba/class-leak": "^2.0",
27+
"tomasvotruba/type-coverage": "^2.0",
28+
"tomasvotruba/unused-public": "^2.0",
2629
"tracy/tracy": "^2.10"
2730
},
2831
"autoload": {

config/sets/symfony/symfony73.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
declare(strict_types=1);
44

55
use Rector\Config\RectorConfig;
6+
use Rector\Symfony\Symfony73\Rector\Class_\InvokableCommandRector;
67

78
// @see https://github.com/symfony/symfony/blame/7.3/UPGRADE-7.3.md
89

910
return RectorConfig::configure()
10-
->withRules([]);
11+
->withRules([InvokableCommandRector::class]);

phpstan.neon

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ parameters:
22
level: 8
33

44
reportUnmatchedIgnoredErrors: false
5-
65
treatPhpDocTypesAsCertain: false
76

87
paths:
@@ -12,18 +11,17 @@ parameters:
1211
- rules
1312
- rules-tests
1413

15-
# to be enabled later once rector upgraded to use phpstan v2
16-
# # https://github.com/rectorphp/type-perfect/
17-
# type_perfect:
18-
# no_mixed: true
19-
# null_over_false: true
20-
# narrow_param: true
21-
# narrow_return: true
14+
# https://github.com/rectorphp/type-perfect/
15+
type_perfect:
16+
no_mixed: true
17+
null_over_false: true
18+
narrow_param: true
19+
narrow_return: true
2220

23-
# unused_public:
24-
# constants: true
25-
# methods: true
26-
# properties: true
21+
unused_public:
22+
constants: true
23+
methods: true
24+
properties: true
2725

2826
scanDirectories:
2927
- stubs
@@ -54,16 +52,12 @@ parameters:
5452
- '#Doing instanceof PHPStan\\Type\\.+ is error\-prone and deprecated#'
5553

5654
# phpstan instanceof
57-
-
58-
identifier: phpstanApi.instanceofAssumption
59-
60-
-
61-
identifier: phpstanApi.varTagAssumption
55+
- identifier: argument.type
56+
- identifier: assign.propertyType
6257

63-
-
64-
identifier: argument.type
58+
- '#::provideMinPhpVersion\(\) never returns \d+ so it can be removed from the return type#'
6559

60+
# node finder
6661
-
67-
identifier: assign.propertyType
68-
69-
- '#::provideMinPhpVersion\(\) never returns \d+ so it can be removed from the return type#'
62+
identifier: return.type
63+
path: rules/Symfony73/NodeAnalyzer/CommandArgumentsAndOptionsResolver.php

rules-tests/Symfony73/Rector/Class_/InvokableCommandRector/Fixture/some_command.php

Lines changed: 0 additions & 32 deletions
This file was deleted.

rules-tests/Symfony73/Rector/Class_/InvokableCommandRector/Fixture/some_command.php.inc

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ final class SomeCommand extends Command
1818
$this->addOption('option', 'o', InputOption::VALUE_NONE, 'Option description');
1919
}
2020

21-
public function execute(InputInterface $input, OutputInterface $output)
21+
public function execute(InputInterface $input, OutputInterface $output): int
2222
{
2323
$someArgument = $input->getArgument('argument');
2424
$someOption = $input->getOption('option');
@@ -35,18 +35,20 @@ final class SomeCommand extends Command
3535

3636
namespace Rector\Symfony\Tests\Symfony73\Rector\Class_\InvokableCommandRector\Fixture;
3737

38+
use Symfony\Component\Console\Attribute\AsCommand;
3839
use Symfony\Component\Console\Command\Command;
39-
use Symfony\Component\Console\Command\Argument;
40-
use Symfony\Component\Console\Command\Option;
40+
use Symfony\Component\Console\Input\InputInterface;
41+
use Symfony\Component\Console\Output\OutputInterface;
42+
use Symfony\Component\Console\Input\InputArgument;
43+
use Symfony\Component\Console\Input\InputOption;
4144

45+
#[AsCommand(name: 'some_name')]
4246
final class SomeCommand
4347
{
44-
public function __invoke(
45-
#[Argument]
46-
string $argument,
47-
#[Option]
48-
bool $option = false,
49-
) {
48+
public function __invoke(#[\Symfony\Component\Console\Attribute\Command\Argument]
49+
string $argument, #[\Symfony\Component\Console\Attribute\Command\Option]
50+
$option): int
51+
{
5052
$someArgument = $argument;
5153
$someOption = $option;
5254

rules/Symfony61/Rector/Class_/CommandConfigureToAttributeRector.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
use PHPStan\Type\ObjectType;
2020
use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory;
2121
use Rector\Rector\AbstractRector;
22-
use Rector\Symfony\Enum\SymfonyAnnotation;
2322
use Rector\Symfony\Enum\SymfonyAttribute;
23+
use Rector\Symfony\Enum\SymfonyClass;
2424
use Rector\ValueObject\PhpVersionFeature;
2525
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
2626
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@@ -103,11 +103,11 @@ public function refactor(Node $node): ?Node
103103
return null;
104104
}
105105

106-
if (! $this->reflectionProvider->hasClass(SymfonyAnnotation::AS_COMMAND)) {
106+
if (! $this->reflectionProvider->hasClass(SymfonyAttribute::AS_COMMAND)) {
107107
return null;
108108
}
109109

110-
if (! $this->isObjectType($node, new ObjectType('Symfony\\Component\\Console\\Command\\Command'))) {
110+
if (! $this->isObjectType($node, new ObjectType(SymfonyClass::COMMAND))) {
111111
return null;
112112
}
113113

@@ -140,7 +140,7 @@ public function refactor(Node $node): ?Node
140140
}
141141

142142
if (! $asCommandAttribute instanceof Attribute) {
143-
$asCommandAttributeGroup = $this->phpAttributeGroupFactory->createFromClass(SymfonyAnnotation::AS_COMMAND);
143+
$asCommandAttributeGroup = $this->phpAttributeGroupFactory->createFromClass(SymfonyAttribute::AS_COMMAND);
144144

145145
$asCommandAttribute = $asCommandAttributeGroup->attrs[0];
146146

rules/Symfony73/NodeAnalyzer/CommandArgumentsAndOptionsResolver.php

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,50 @@
66

77
use PhpParser\Node;
88
use PhpParser\Node\Expr\MethodCall;
9+
use PhpParser\Node\Identifier;
910
use PhpParser\Node\Scalar\String_;
1011
use PhpParser\Node\Stmt\ClassMethod;
1112
use PhpParser\NodeFinder;
1213
use Rector\Exception\ShouldNotHappenException;
13-
use Rector\Symfony\Symfony73\ValueObject\CommandOptionMetadata;
14+
use Rector\Symfony\Symfony73\ValueObject\CommandArgument;
15+
use Rector\Symfony\Symfony73\ValueObject\CommandOption;
1416

1517
final class CommandArgumentsAndOptionsResolver
1618
{
1719
/**
18-
* @return CommandOptionMetadata[]
20+
* @return CommandArgument[]
1921
*/
20-
public function collectCommandOptionsMetadatas(ClassMethod $configureClassMethod): array
22+
public function collectCommandArguments(ClassMethod $configureClassMethod): array
23+
{
24+
$addArgumentMethodCalls = $this->findMethodCallsByName($configureClassMethod, 'addArgument');
25+
26+
$commandArguments = [];
27+
foreach ($addArgumentMethodCalls as $addArgumentMethodCall) {
28+
// @todo extract name, type and requirements
29+
$addArgumentArgs = $addArgumentMethodCall->getArgs();
30+
31+
$nameArgValue = $addArgumentArgs[0]->value;
32+
if (! $nameArgValue instanceof String_) {
33+
// we need string value, otherwise param will not have a name
34+
throw new ShouldNotHappenException('Argument name is required');
35+
}
36+
37+
$optionName = $nameArgValue->value;
38+
39+
$commandArguments[] = new CommandArgument($optionName);
40+
}
41+
42+
return $commandArguments;
43+
}
44+
45+
/**
46+
* @return CommandOption[]
47+
*/
48+
public function collectCommandOptions(ClassMethod $configureClassMethod): array
2149
{
2250
$addOptionMethodCalls = $this->findMethodCallsByName($configureClassMethod, 'addOption');
2351

2452
$commandOptionMetadatas = [];
25-
2653
foreach ($addOptionMethodCalls as $addOptionMethodCall) {
2754
// @todo extract name, type and requirements
2855
$addOptionArgs = $addOptionMethodCall->getArgs();
@@ -35,7 +62,7 @@ public function collectCommandOptionsMetadatas(ClassMethod $configureClassMethod
3562

3663
$optionName = $nameArgValue->value;
3764

38-
$commandOptionMetadatas[] = new CommandOptionMetadata($optionName);
65+
$commandOptionMetadatas[] = new CommandOption($optionName);
3966
}
4067

4168
return $commandOptionMetadatas;
@@ -53,7 +80,7 @@ private function findMethodCallsByName(ClassMethod $classMethod, string $desired
5380
return false;
5481
}
5582

56-
if (! $node->name instanceof Node\Identifier) {
83+
if (! $node->name instanceof Identifier) {
5784
return false;
5885
}
5986

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Symfony\Symfony73\NodeFactory;
6+
7+
use PhpParser\Node\Attribute;
8+
use PhpParser\Node\AttributeGroup;
9+
use PhpParser\Node\Expr\Variable;
10+
use PhpParser\Node\Identifier;
11+
use PhpParser\Node\Name\FullyQualified;
12+
use PhpParser\Node\Param;
13+
use Rector\Symfony\Enum\SymfonyAttribute;
14+
use Rector\Symfony\Symfony73\ValueObject\CommandArgument;
15+
use Rector\Symfony\Symfony73\ValueObject\CommandOption;
16+
17+
final class CommandInvokeParamsFactory
18+
{
19+
/**
20+
* @param CommandArgument[] $commandArguments
21+
* @param CommandOption[] $commandOptions
22+
* @return Param[]
23+
*/
24+
public function createParams(array $commandArguments, array $commandOptions): array
25+
{
26+
$argumentParams = $this->createArgumentParams($commandArguments);
27+
$optionParams = $this->createOptionParams($commandOptions);
28+
29+
return array_merge($argumentParams, $optionParams);
30+
}
31+
32+
/**
33+
* @param CommandArgument[] $commandArguments
34+
* @return Param[]
35+
*/
36+
private function createArgumentParams(array $commandArguments): array
37+
{
38+
$argumentParams = [];
39+
40+
foreach ($commandArguments as $commandArgument) {
41+
$argumentParam = new Param(new Variable($commandArgument->getName()));
42+
43+
$argumentParam->type = new Identifier('string');
44+
// @todo fill type or default value
45+
// @todo default string, multiple values array
46+
47+
$argumentParam->attrGroups[] = new AttributeGroup([
48+
new Attribute(new FullyQualified(SymfonyAttribute::COMMAND_ARGUMENT)),
49+
]);
50+
51+
$argumentParams[] = $argumentParam;
52+
}
53+
54+
return $argumentParams;
55+
}
56+
57+
/**
58+
* @param CommandOption[] $commandOptions
59+
* @return Param[]
60+
*/
61+
private function createOptionParams(array $commandOptions): array
62+
{
63+
$optionParams = [];
64+
65+
foreach ($commandOptions as $commandOption) {
66+
$optionParam = new Param(new Variable($commandOption->getName()));
67+
68+
// @todo fill type or default value
69+
$optionParam->attrGroups[] = new AttributeGroup([
70+
new Attribute(new FullyQualified(SymfonyAttribute::COMMAND_OPTION)),
71+
]);
72+
73+
$optionParams[] = $optionParam;
74+
}
75+
76+
return $optionParams;
77+
}
78+
}

0 commit comments

Comments
 (0)