mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-08 20:22:24 +00:00
[DX] Add PhpUpgradeDowngradeRegisteredInSetRule PHPStan rule (#905)
* [DX] Add PhpUpgradeDowngradeRegisteredInSetRule PHPStan rule * phpstan * register test * [ci-review] Rector Rectify * phpstan * phpstan fix * check prefix * Fix * fix * final touch: service config * skip implements ConfigurableRectorInterface * [ci-review] Rector Rectify * [ci-review] Rector Rectify * register services * final touch: example of non-configurable service * include configured rector Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
parent
e78fadd24d
commit
e09cf113d7
1
.github/workflows/tests.yaml
vendored
1
.github/workflows/tests.yaml
vendored
|
@ -20,6 +20,7 @@ jobs:
|
|||
- tests
|
||||
- rules-tests
|
||||
- packages-tests
|
||||
- utils/phpstan-rules/tests
|
||||
|
||||
name: PHP ${{ matrix.php }} tests for ${{ matrix.path }}
|
||||
steps:
|
||||
|
|
|
@ -93,7 +93,9 @@
|
|||
"rules-tests"
|
||||
],
|
||||
"Rector\\Core\\Tests\\": "tests",
|
||||
"Rector\\RuleDocGenerator\\": "utils/rule-doc-generator/src"
|
||||
"Rector\\RuleDocGenerator\\": "utils/rule-doc-generator/src",
|
||||
"Rector\\PHPStanRules\\": "utils/phpstan-rules/src",
|
||||
"Rector\\PHPStanRules\\Tests\\": "utils/phpstan-rules/tests"
|
||||
},
|
||||
"classmap": [
|
||||
"stubs",
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
declare(strict_types=1);
|
||||
|
||||
use Rector\Php55\Rector\Class_\ClassConstantToSelfClassRector;
|
||||
use Rector\Php55\Rector\FuncCall\PregReplaceEModifierRector;
|
||||
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
|
@ -10,4 +11,5 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
$services = $containerConfigurator->services();
|
||||
$services->set(StringClassNameToClassConstantRector::class);
|
||||
$services->set(ClassConstantToSelfClassRector::class);
|
||||
$services->set(PregReplaceEModifierRector::class);
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
use Rector\Php71\Rector\Assign\AssignArrayToStringRector;
|
||||
use Rector\Php71\Rector\BinaryOp\BinaryOpBetweenNumberAndStringRector;
|
||||
use Rector\Php71\Rector\BooleanOr\IsIterableRector;
|
||||
use Rector\Php71\Rector\ClassConst\PublicConstantVisibilityRector;
|
||||
use Rector\Php71\Rector\FuncCall\CountOnNullRector;
|
||||
use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector;
|
||||
use Rector\Php71\Rector\List_\ListToArrayDestructRector;
|
||||
|
@ -20,4 +21,5 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
$services->set(RemoveExtraParametersRector::class);
|
||||
$services->set(BinaryOpBetweenNumberAndStringRector::class);
|
||||
$services->set(ListToArrayDestructRector::class);
|
||||
$services->set(PublicConstantVisibilityRector::class);
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ use Rector\Php73\Rector\FuncCall\ArrayKeyFirstLastRector;
|
|||
use Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector;
|
||||
use Rector\Php73\Rector\FuncCall\RegexDashEscapeRector;
|
||||
use Rector\Php73\Rector\FuncCall\SensitiveDefineRector;
|
||||
use Rector\Php73\Rector\FuncCall\SetCookieRector;
|
||||
use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector;
|
||||
use Rector\Php73\Rector\String_\SensitiveHereNowDocRector;
|
||||
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
|
||||
|
@ -47,4 +48,5 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
$services->set(JsonThrowOnErrorRector::class);
|
||||
$services->set(RegexDashEscapeRector::class);
|
||||
$services->set(ContinueToBreakInSwitchRector::class);
|
||||
$services->set(SetCookieRector::class);
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
declare(strict_types=1);
|
||||
|
||||
use Rector\Php81\Rector\Class_\MyCLabsClassToEnumRector;
|
||||
use Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector;
|
||||
use Rector\Php81\Rector\ClassConst\FinalizePublicClassConstantRector;
|
||||
use Rector\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector;
|
||||
use Rector\Php81\Rector\Property\ReadOnlyPropertyRector;
|
||||
|
@ -16,4 +17,5 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
$services->set(MyCLabsMethodCallToEnumConstRector::class);
|
||||
$services->set(FinalizePublicClassConstantRector::class);
|
||||
$services->set(ReadOnlyPropertyRector::class);
|
||||
$services->set(SpatieEnumClassToEnumRector::class);
|
||||
};
|
||||
|
|
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\BetterPhpDocParser\ValueObject\Type;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\NodeAttributes;
|
||||
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
|
@ -12,8 +11,6 @@ use Stringable;
|
|||
|
||||
final class SpacingAwareCallableTypeNode extends CallableTypeNode implements Stringable
|
||||
{
|
||||
use NodeAttributes;
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
// keep original (Psalm?) format, see https://github.com/rectorphp/rector/issues/2841
|
||||
|
|
|
@ -12,6 +12,11 @@ includes:
|
|||
- vendor/symplify/phpstan-rules/config/symfony-rules.neon
|
||||
- vendor/symplify/phpstan-rules/config/test-rules.neon
|
||||
|
||||
services:
|
||||
-
|
||||
class: Rector\PHPStanRules\Rules\PhpUpgradeDowngradeRegisteredInSetRule
|
||||
tags: [phpstan.rules.rule]
|
||||
|
||||
parameters:
|
||||
level: max
|
||||
|
||||
|
@ -509,3 +514,6 @@ parameters:
|
|||
- rules/Php74/Rector/LNumber/AddLiteralSeparatorToNumberRector.php
|
||||
|
||||
- '#^Cognitive complexity for "Rector\\CodingStyle\\Naming\\NameRenamer\:\:renameNameNode\(\)" is 13, keep it under 9$#'
|
||||
- '#Register Rector\\Php71\\Rector\\Name\\ReservedObjectRector to php71 config set#'
|
||||
- '#Register Rector\\Php80\\Rector\\Class_\\AnnotationToAttributeRector to php80 config set#'
|
||||
- '#Register Rector\\Php80\\Rector\\Class_\\DoctrineAnnotationClassToAttributeRector to php80 config set#'
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanRules\Rules;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use Symplify\PHPStanRules\Rules\AbstractSymplifyRule;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
/**
|
||||
* @see \Rector\PHPStanRules\Tests\Rules\PhpUpgradeDowngradeRegisteredInSetRule\PhpUpgradeDowngradeRegisteredInSetRuleTest
|
||||
*/
|
||||
final class PhpUpgradeDowngradeRegisteredInSetRule extends AbstractSymplifyRule
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const ERROR_MESSAGE = 'Register %s to %s config set';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @see https://regex101.com/r/C3nz6e/1/
|
||||
*/
|
||||
private const PREFIX_REGEX = '#(Downgrade)?Php\d+#';
|
||||
|
||||
public function __construct(
|
||||
private SmartFileSystem $smartFileSystem
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
* @return string[]
|
||||
*/
|
||||
public function process(Node $node, Scope $scope): array
|
||||
{
|
||||
/** @var string $className */
|
||||
$className = (string) $node->namespacedName;
|
||||
if (! str_ends_with($className, 'Rector')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
[, $prefix] = explode('\\', $className);
|
||||
if (! Strings::match($prefix, self::PREFIX_REGEX)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$phpVersion = Strings::substring($prefix, -2);
|
||||
|
||||
$configFile = str_starts_with($prefix, 'Downgrade')
|
||||
? 'downgrade-php' . $phpVersion
|
||||
: 'php' . $phpVersion;
|
||||
|
||||
$configContent = $this->smartFileSystem->readFile(
|
||||
__DIR__ . '/../../../../config/set/' . $configFile . '.php'
|
||||
);
|
||||
|
||||
$shortClassName = (string) $node->name;
|
||||
$toSearch = sprintf('$services->set(%s::class)', $shortClassName);
|
||||
|
||||
if (! str_contains($configContent, $toSearch)) {
|
||||
return [sprintf(self::ERROR_MESSAGE, $className, $configFile)];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition(self::ERROR_MESSAGE, [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
// config/set/php74.php
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
// config/set/php74.php
|
||||
$services->set(RealToFloatTypeCastRector::class);
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Arguments\Rector\ClassMethod;
|
||||
|
||||
final class SkipSomePhpFeatureRector
|
||||
{
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\DowngradePhp80\Rector\Class_;
|
||||
|
||||
final class SomePhpFeature2Rector
|
||||
{
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Php80\Rector\Class_;
|
||||
|
||||
final class SomePhpFeatureRector
|
||||
{
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanRules\Tests\Rules\PhpUpgradeDowngradeRegisteredInSetRule;
|
||||
|
||||
use Iterator;
|
||||
use PHPStan\Rules\Rule;
|
||||
use Rector\PHPStanRules\Rules\PhpUpgradeDowngradeRegisteredInSetRule;
|
||||
use Symplify\PHPStanExtensions\Testing\AbstractServiceAwareRuleTestCase;
|
||||
|
||||
/**
|
||||
* @extends AbstractServiceAwareRuleTestCase<PhpUpgradeDowngradeRegisteredInSetRule>
|
||||
*/
|
||||
final class PhpUpgradeDowngradeRegisteredInSetRuleTest extends AbstractServiceAwareRuleTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
* @param array<string|int> $expectedErrorMessagesWithLines
|
||||
*/
|
||||
public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void
|
||||
{
|
||||
$this->analyse([$filePath], $expectedErrorMessagesWithLines);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<string[]|array<int, mixed[]>>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
yield [__DIR__ . '/Fixture/SkipSomePhpFeatureRector.php', []];
|
||||
yield [__DIR__ . '/Fixture/SomePhpFeatureRector.php', [
|
||||
[sprintf(PhpUpgradeDowngradeRegisteredInSetRule::ERROR_MESSAGE, 'Rector\Php80\Rector\Class_\SomePhpFeatureRector', 'php80'), 7]
|
||||
]];
|
||||
yield [__DIR__ . '/Fixture/SomePhpFeature2Rector.php', [
|
||||
[sprintf(PhpUpgradeDowngradeRegisteredInSetRule::ERROR_MESSAGE, 'Rector\DowngradePhp80\Rector\Class_\SomePhpFeature2Rector', 'downgrade-php80'), 7]
|
||||
]];
|
||||
}
|
||||
|
||||
protected function getRule(): Rule
|
||||
{
|
||||
return $this->getRuleFromConfig(
|
||||
PhpUpgradeDowngradeRegisteredInSetRule::class,
|
||||
__DIR__ . '/config/configured_rule.neon'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
services:
|
||||
- Symplify\SmartFileSystem\SmartFileSystem
|
||||
-
|
||||
class: Rector\PHPStanRules\Rules\PhpUpgradeDowngradeRegisteredInSetRule
|
||||
tags: [phpstan.rules.rule]
|
Loading…
Reference in New Issue
Block a user