Skip to content

Commit

Permalink
[DX] Add PhpUpgradeDowngradeRegisteredInSetRule PHPStan rule (#905)
Browse files Browse the repository at this point in the history
* [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>
  • Loading branch information
samsonasik and actions-user committed Sep 21, 2021
1 parent e78fadd commit e09cf11
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
- tests
- rules-tests
- packages-tests
- utils/phpstan-rules/tests

name: PHP ${{ matrix.php }} tests for ${{ matrix.path }}
steps:
Expand Down
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions config/set/php55.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
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;

return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(StringClassNameToClassConstantRector::class);
$services->set(ClassConstantToSelfClassRector::class);
$services->set(PregReplaceEModifierRector::class);
};
2 changes: 2 additions & 0 deletions config/set/php71.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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;
Expand All @@ -20,4 +21,5 @@
$services->set(RemoveExtraParametersRector::class);
$services->set(BinaryOpBetweenNumberAndStringRector::class);
$services->set(ListToArrayDestructRector::class);
$services->set(PublicConstantVisibilityRector::class);
};
2 changes: 2 additions & 0 deletions config/set/php73.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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;
Expand Down Expand Up @@ -47,4 +48,5 @@
$services->set(JsonThrowOnErrorRector::class);
$services->set(RegexDashEscapeRector::class);
$services->set(ContinueToBreakInSwitchRector::class);
$services->set(SetCookieRector::class);
};
2 changes: 2 additions & 0 deletions config/set/php81.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -16,4 +17,5 @@
$services->set(MyCLabsMethodCallToEnumConstRector::class);
$services->set(FinalizePublicClassConstantRector::class);
$services->set(ReadOnlyPropertyRector::class);
$services->set(SpatieEnumClassToEnumRector::class);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@

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;
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
Expand Down
8 changes: 8 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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#'
Original file line number Diff line number Diff line change
@@ -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
),
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Rector\Arguments\Rector\ClassMethod;

final class SkipSomePhpFeatureRector
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Rector\DowngradePhp80\Rector\Class_;

final class SomePhpFeature2Rector
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Rector\Php80\Rector\Class_;

final class SomePhpFeatureRector
{
}
Original file line number Diff line number Diff line change
@@ -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'
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
services:
- Symplify\SmartFileSystem\SmartFileSystem
-
class: Rector\PHPStanRules\Rules\PhpUpgradeDowngradeRegisteredInSetRule
tags: [phpstan.rules.rule]

0 comments on commit e09cf11

Please sign in to comment.