[CI] simplify php linter (#5867)

* [CI] simplify php linter

* [CI] merge code analysis to one

* [docs] remove nodes overview, already in external repository

* remove unused ci job

* [CI] check annotation parser + sync

* [DeadCode] Remove RemoveEmptyAbstractClassRector, not reliable with static-reflection, opionated

* [CI] remove set validation, PHPStan handles this better with PHP

* remove dead-code-strict [skip ci]
This commit is contained in:
Tomas Votruba 2021-03-15 23:44:28 +01:00 committed by GitHub
parent 68bf6e8de8
commit 683f8319a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 338 additions and 3776 deletions

View File

@ -18,15 +18,22 @@ jobs:
name: Validate Fixtures skip file prefix
run: vendor/symplify/easy-testing/bin/easy-testing validate-fixture-skip-naming rules tests
-
name: 'Validate Sets Loading'
# this is very slow, so it has to be in own workflow
run: bin/rector validate-sets --ansi
-
name: 'Validate Max File Length'
run: vendor/bin/easy-ci validate-file-length packages rules src tests
-
name: 'PHPStan'
run: vendor/bin/phpstan analyse --ansi --error-format symplify
-
name: 'PHPStan for config'
run: composer phpstan-config
-
name: "Check doctrine/annotation"
run: "bin/rector sync-annotation-parser --dry-run --ansi"
# see https://github.com/rectorphp/rector-generator
-
name: 'Rector Generate From Recipe'

View File

@ -1,35 +0,0 @@
name: Code Analysis PHP 8.0
on:
pull_request: null
jobs:
code_analysis_php80:
strategy:
fail-fast: false
matrix:
actions:
-
name: 'PHPStan'
run: vendor/bin/phpstan analyse --ansi --error-format symplify
-
name: 'PHPStan for config'
run: composer phpstan-config
name: ${{ matrix.actions.name }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# see https://github.com/shivammathur/setup-php
-
uses: shivammathur/setup-php@v2
with:
php-version: 8.0
coverage: none
- uses: "ramsey/composer-install@v1"
- run: ${{ matrix.actions.run }}

View File

@ -1,58 +0,0 @@
name: Daily Pull Requests
on:
schedule:
# https://crontab.guru/once-a-day
- cron: "0 0 * * *"
jobs:
daily_pull_requests:
strategy:
fail-fast: false
matrix:
actions:
-
name: "Re-Generate Doctrine Annotation Parser"
run: "bin/rector sync-annotation-parser --dry-run --ansi"
branch: 'automated-regenerate-doctrine-parser'
-
name: "Re-Propagate Monorepo Dependencies"
run: "composer propagate --ansi"
branch: 'automated-propagate-monorepo-dependencies'
name: ${{ matrix.actions.name }}
runs-on: ubuntu-latest
steps:
-
uses: actions/checkout@v2
with:
token: ${{ secrets.ACCESS_TOKEN }}
# see https://github.com/shivammathur/setup-php
-
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
coverage: none
-
run: composer install --no-progress --ansi
-
run: ${{ matrix.actions.run }}
# see https://github.com/peter-evans/create-pull-request
-
name: Create pull-request
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "[automated] ${{ matrix.actions.name }}"
base: 'main'
branch: ${{ matrix.actions.branch }}
title: '[automated] ${{ matrix.actions.name }}'
delete-branch: true

View File

@ -5,30 +5,17 @@ on:
jobs:
php_linter:
strategy:
fail-fast: false
matrix:
actions:
-
php_version: 7.3
run: vendor/bin/parallel-lint src bin/rector config tests packages rules --colors --exclude packages/rector-generator/templates --exclude rules/psr4/tests/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Source --exclude rules/autodiscovery/tests/Rector/FileNode/MoveInterfacesToContractNamespaceDirectoryRector/Expected --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativeProps.php --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativePropsPhp80.php --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithTypedPropertyTypes.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/Source/ExpectedSomeClassCopyEvent.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceMagicPropertyEventWithEventClassRector/Source/ExpectedFileManagerUploadEvent.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceMagicPropertyEventWithEventClassRector/Source/ExpectedDuplicatedEventParamsUploadEvent.php --exclude rules/type-declaration/tests/Rector/ClassMethod/ParamTypeFromStrictTypedPropertyRector/Source/OutOfControlExternalClass.php
-
php_version: 8.0
run: vendor/bin/parallel-lint src bin/rector config tests packages rules --colors --exclude packages/rector-generator/templates
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# see https://github.com/shivammathur/setup-php
-
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.actions.php_version }}
php-version: 7.3
coverage: none
- uses: "ramsey/composer-install@v1"
- run: composer create-project php-parallel-lint/php-parallel-lint php-parallel-lint
- run: ${{ matrix.actions.run }}
- run: php-parallel-lint/parallel-lint src bin/rector config tests packages rules --colors --exclude rules/psr4/tests/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Source --exclude rules/autodiscovery/tests/Rector/FileNode/MoveInterfacesToContractNamespaceDirectoryRector/Expected --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/Source/ExpectedSomeClassCopyEvent.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceMagicPropertyEventWithEventClassRector/Source --exclude rules/type-declaration/tests/Rector/ClassMethod/ParamTypeFromStrictTypedPropertyRector/Source

View File

@ -65,7 +65,6 @@
"nette/application": "^3.0.7",
"nette/di": "^3.0",
"nette/forms": "^3.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-nette": "^0.12.16",
"phpunit/phpunit": "^9.5",
"rector/rector-generator": "^0.1.1",
@ -96,8 +95,7 @@
"Rector\\RuleDocGenerator\\": "utils/rule-doc-generator/src",
"Rector\\PHPStanExtensions\\": "utils/phpstan-extensions/src",
"Rector\\PHPStanExtensions\\Tests\\": "utils/phpstan-extensions/tests",
"Rector\\Utils\\DoctrineAnnotationParserSyncer\\": "utils/doctrine-annotation-parser-syncer/src",
"Rector\\Utils\\ProjectValidator\\": "utils/project-validator/src"
"Rector\\Utils\\DoctrineAnnotationParserSyncer\\": "utils/doctrine-annotation-parser-syncer/src"
},
"classmap": [
"rules-tests/Autodiscovery/Rector/FileNode/MoveInterfacesToContractNamespaceDirectoryRector/Expected",
@ -139,7 +137,7 @@
"phpstan-config": "vendor/bin/phpstan analyse config --ansi --error-format symplify",
"docs": [
"vendor/bin/rule-doc-generator generate packages rules --output-file docs/rector_rules_overview.md --ansi --categorize",
"vendor/bin/ecs check-markdown docs/rector_rules_overview.md docs/nodes_overview.md --ansi --fix"
"vendor/bin/ecs check-markdown docs/rector_rules_overview.md --ansi --fix"
],
"rector": "bin/rector process --ansi"
},

View File

@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
use Rector\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPublicMethodRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(RemoveUnusedPublicMethodRector::class);
$services->set(RemoveEmptyAbstractClassRector::class);
};

View File

@ -10,7 +10,6 @@ use Rector\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector;
use Rector\DeadCode\Rector\BinaryOp\RemoveDuplicatedInstanceOfRector;
use Rector\DeadCode\Rector\BooleanAnd\RemoveAndTrueRector;
use Rector\DeadCode\Rector\Cast\RecastingRemovalRector;
use Rector\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector;
use Rector\DeadCode\Rector\Class_\RemoveUnusedDoctrineEntityMethodAndPropertyRector;
use Rector\DeadCode\Rector\ClassConst\RemoveUnusedClassConstantRector;
use Rector\DeadCode\Rector\ClassConst\RemoveUnusedPrivateConstantRector;
@ -101,7 +100,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(RemoveDeadConditionAboveReturnRector::class);
$services->set(RemoveUnusedConstructorParamRector::class);
$services->set(RemoveDeadInstanceOfRector::class);
$services->set(RemoveEmptyAbstractClassRector::class);
$services->set(RemoveDeadLoopRector::class);
// docblock

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
# 661 Rules Overview
# 659 Rules Overview
<br>
@ -20,7 +20,7 @@
- [Composer](#composer) (5)
- [DeadCode](#deadcode) (49)
- [DeadCode](#deadcode) (48)
- [DeadDocBlock](#deaddocblock) (5)
@ -112,7 +112,7 @@
- [Restoration](#restoration) (8)
- [Symfony](#symfony) (14)
- [Symfony](#symfony) (13)
- [Symfony2](#symfony2) (3)
@ -3509,35 +3509,6 @@ Remove duplicated instanceof in one call
<br>
### RemoveEmptyAbstractClassRector
Empty abstract class that does nothing
- class: [`Rector\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector`](../rules/DeadCode/Rector/Class_/RemoveEmptyAbstractClassRector.php)
```diff
-class SomeClass extends SomeAbstractClass
+class SomeClass extends AnotherAbstractClass
{
}
-abstract class SomeAbstractClass extends AnotherAbstractClass
+abstracst clas AnotherAbstractClass
{
-}
-
-abstract class AnotherAbstractClass
-{
public function getName()
{
- return 'name';
+ return 'cowo';
}
}
```
<br>
### RemoveEmptyClassMethodRector
Remove empty method calls not required by parents
@ -13651,25 +13622,6 @@ Make Symfony commands lazy
<br>
### NormalizeAutowireMethodNamingRector
Use autowire + class name suffix for method with `@required` annotation
- class: [`Rector\Symfony\Rector\ClassMethod\NormalizeAutowireMethodNamingRector`](../rules/Symfony/Rector/ClassMethod/NormalizeAutowireMethodNamingRector.php)
```diff
class SomeClass
{
/** @required */
- public function foo()
+ public function autowireSomeClass()
{
}
}
```
<br>
### RemoveServiceFromSensioRouteRector
Remove service from Sensio `@Route`

View File

@ -3,7 +3,15 @@
namespace Rector\DoctrineAnnotationGenerated;
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
use Doctrine\Common\Annotations\Annotation\Target;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
use ReflectionProperty;
use function array_merge;
use function class_exists;
use function extension_loaded;
use function ini_get;
/**
* A reader for docblock annotations.
*/
@ -14,10 +22,7 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
*
* @var array<string, class-string>
*/
private static $globalImports = [
'ignoreannotation' => \Doctrine\Common\Annotations\Annotation\IgnoreAnnotation::class,
];
private static $globalImports = ['ignoreannotation' => \Doctrine\Common\Annotations\Annotation\IgnoreAnnotation::class];
/**
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
*
@ -26,7 +31,6 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
* @var array<string, true>
*/
private static $globalIgnoredNames = \Doctrine\Common\Annotations\ImplicitlyIgnoredAnnotationNames::LIST;
/**
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
*
@ -35,50 +39,62 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
* @var array<string, true>
*/
private static $globalIgnoredNamespaces = [];
/**
* Add a new annotation to the globally ignored annotation names with regard to exception handling.
*
* @param string $name
*/
public static function addGlobalIgnoredName($name)
{
self::$globalIgnoredNames[$name] = true;
}
/**
* Add a new annotation to the globally ignored annotation namespaces with regard to exception handling.
*
* @param string $namespace
*/
public static function addGlobalIgnoredNamespace($namespace)
{
self::$globalIgnoredNamespaces[$namespace] = true;
}
/**
* Annotations parser.
*
* @var DocParser
*/
private $parser;
/**
* Annotations parser used to collect parsing metadata.
*
* @var DocParser
*/
private $preParser;
/**
* PHP parser used to collect imports.
*
* @var PhpParser
*/
private $phpParser;
/**
* In-memory cache mechanism to store imported annotations per class.
*
* @var array<string, array<string, class-string>>
* @psalm-var array<'class'|'function', array<string, array<string, class-string>>>
*/
private $imports = [];
/**
* In-memory cache mechanism to store ignored annotations per class.
*
* @var array<string, array<string, true>>
* @psalm-var array<'class'|'function', array<string, array<string, true>>>
*/
private $ignoredAnnotationNames = [];
/**
* Initializes a new AnnotationReader.
*
* @throws AnnotationException
*/
public function __construct(\Rector\DoctrineAnnotationGenerated\ConstantPreservingDocParser $parser = null)
{
if (\extension_loaded('Zend Optimizer+') && (\ini_get('zend_optimizerplus.save_comments') === '0' || \ini_get(
'opcache.save_comments'
) === '0')) {
if (\extension_loaded('Zend Optimizer+') && (\ini_get('zend_optimizerplus.save_comments') === '0' || \ini_get('opcache.save_comments') === '0')) {
throw \Doctrine\Common\Annotations\AnnotationException::optimizerPlusSaveComments();
}
if (\extension_loaded('Zend OPcache') && \ini_get('opcache.save_comments') === 0) {
@ -93,39 +109,17 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
$this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames);
$this->phpParser = new \Doctrine\Common\Annotations\PhpParser();
}
/**
* Add a new annotation to the globally ignored annotation names with regard to exception handling.
*
* @param string $name
*/
public static function addGlobalIgnoredName($name)
{
self::$globalIgnoredNames[$name] = true;
}
/**
* Add a new annotation to the globally ignored annotation namespaces with regard to exception handling.
*
* @param string $namespace
*/
public static function addGlobalIgnoredNamespace($namespace)
{
self::$globalIgnoredNamespaces[$namespace] = true;
}
/**
* {@inheritDoc}
*/
public function getClassAnnotations(\ReflectionClass $class)
{
$this->parser->setTarget(\Doctrine\Common\Annotations\Annotation\Target::TARGET_CLASS);
$this->parser->setImports($this->getClassImports($class));
$this->parser->setImports($this->getImports($class));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
}
/**
* {@inheritDoc}
*/
@ -139,7 +133,6 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
}
return null;
}
/**
* {@inheritDoc}
*/
@ -153,7 +146,6 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($property->getDocComment(), $context);
}
/**
* {@inheritDoc}
*/
@ -167,7 +159,6 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
}
return null;
}
/**
* {@inheritDoc}
*/
@ -181,7 +172,6 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($method->getDocComment(), $context);
}
/**
* {@inheritDoc}
*/
@ -195,37 +185,69 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
}
return null;
}
/**
* Returns the ignored annotations for the given class.
* Gets the annotations applied to a function.
*
* @phpstan-return list<object> An array of Annotations.
*/
public function getFunctionAnnotations(\ReflectionFunction $function): array
{
$context = 'function ' . $function->getName();
$this->parser->setTarget(\Doctrine\Common\Annotations\Annotation\Target::TARGET_FUNCTION);
$this->parser->setImports($this->getImports($function));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($function->getDocComment(), $context);
}
/**
* Gets a function annotation.
*
* @return object|null The Annotation or NULL, if the requested annotation does not exist.
*/
public function getFunctionAnnotation(\ReflectionFunction $function, string $annotationName)
{
$annotations = $this->getFunctionAnnotations($function);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
/**
* Returns the ignored annotations for the given class or function.
*
* @param ReflectionClass|ReflectionFunction $reflection
*
* @return array<string, true>
*/
private function getIgnoredAnnotationNames(\ReflectionClass $class)
private function getIgnoredAnnotationNames($reflection): array
{
$name = $class->getName();
if (isset($this->ignoredAnnotationNames[$name])) {
return $this->ignoredAnnotationNames[$name];
$type = $reflection instanceof \ReflectionClass ? 'class' : 'function';
$name = $reflection->getName();
if (isset($this->ignoredAnnotationNames[$type][$name])) {
return $this->ignoredAnnotationNames[$type][$name];
}
$this->collectParsingMetadata($class);
return $this->ignoredAnnotationNames[$name];
$this->collectParsingMetadata($reflection);
return $this->ignoredAnnotationNames[$type][$name];
}
/**
* Retrieves imports.
* Retrieves imports for a class or a function.
*
* @param ReflectionClass|ReflectionFunction $reflection
*
* @return array<string, class-string>
*/
private function getClassImports(\ReflectionClass $class)
private function getImports($reflection): array
{
$name = $class->getName();
if (isset($this->imports[$name])) {
return $this->imports[$name];
$type = $reflection instanceof \ReflectionClass ? 'class' : 'function';
$name = $reflection->getName();
if (isset($this->imports[$type][$name])) {
return $this->imports[$type][$name];
}
$this->collectParsingMetadata($class);
return $this->imports[$name];
$this->collectParsingMetadata($reflection);
return $this->imports[$type][$name];
}
/**
* Retrieves imports for methods.
*
@ -234,17 +256,16 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
private function getMethodImports(\ReflectionMethod $method)
{
$class = $method->getDeclaringClass();
$classImports = $this->getClassImports($class);
$classImports = $this->getImports($class);
$traitImports = [];
foreach ($class->getTraits() as $trait) {
if (! $trait->hasMethod($method->getName()) || $trait->getFileName() !== $method->getFileName()) {
if (!$trait->hasMethod($method->getName()) || $trait->getFileName() !== $method->getFileName()) {
continue;
}
$traitImports = \array_merge($traitImports, $this->phpParser->parseClass($trait));
$traitImports = \array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
}
return \array_merge($classImports, $traitImports);
}
/**
* Retrieves imports for properties.
*
@ -253,36 +274,36 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
private function getPropertyImports(\ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
$classImports = $this->getClassImports($class);
$classImports = $this->getImports($class);
$traitImports = [];
foreach ($class->getTraits() as $trait) {
if (! $trait->hasProperty($property->getName())) {
if (!$trait->hasProperty($property->getName())) {
continue;
}
$traitImports = \array_merge($traitImports, $this->phpParser->parseClass($trait));
$traitImports = \array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
}
return \array_merge($classImports, $traitImports);
}
/**
* Collects parsing metadata for a given class.
* Collects parsing metadata for a given class or function.
*
* @param ReflectionClass|ReflectionFunction $reflection
*/
private function collectParsingMetadata(\ReflectionClass $class)
private function collectParsingMetadata($reflection): void
{
$type = $reflection instanceof \ReflectionClass ? 'class' : 'function';
$name = $reflection->getName();
$ignoredAnnotationNames = self::$globalIgnoredNames;
$annotations = $this->preParser->parse($class->getDocComment(), 'class ' . $class->name);
$annotations = $this->preParser->parse($reflection->getDocComment(), $type . ' ' . $name);
foreach ($annotations as $annotation) {
if (! $annotation instanceof \Doctrine\Common\Annotations\Annotation\IgnoreAnnotation) {
if (!$annotation instanceof \Doctrine\Common\Annotations\Annotation\IgnoreAnnotation) {
continue;
}
foreach ($annotation->names as $annot) {
$ignoredAnnotationNames[$annot] = true;
}
}
$name = $class->getName();
$this->imports[$name] = \array_merge(self::$globalImports, $this->phpParser->parseClass($class), [
'__NAMESPACE__' => $class->getNamespaceName(), 'self' => $name,
]);
$this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames;
$this->imports[$type][$name] = \array_merge(self::$globalImports, $this->phpParser->parseUseStatements($reflection), ['__NAMESPACE__' => $reflection->getNamespaceName(), 'self' => $name]);
$this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames;
}
}

View File

@ -98,11 +98,6 @@ final class SetList implements SetListInterface
*/
public const DEAD_CODE = __DIR__ . '/../../../config/set/dead-code.php';
/**
* @var string
*/
public const DEAD_CODE_STRICT = __DIR__ . '/../../../config/set/dead-code-strict.php';
/**
* @var string
*/

View File

@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector;
use Iterator;
use Rector\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class ExtraFilesTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
* @param SmartFileInfo[] $extraFileInfos
*/
public function test(SmartFileInfo $originalFileInfo, array $extraFileInfos = []): void
{
$this->doTestFileInfo($originalFileInfo, $extraFileInfos);
}
/**
* @return Iterator<array<int, SmartFileInfo[]>|SmartFileInfo[]>
*/
public function provideData(): Iterator
{
$extraFileInfos = [new SmartFileInfo(__DIR__ . '/Source/UseAbstract.php')];
yield [new SmartFileInfo(__DIR__ . '/FixtureExtraFiles/SkipUsedAbstractClass.php.inc'), $extraFileInfos];
$extraFileInfos = [
new SmartFileInfo(__DIR__ . '/Source/AbstractMain.php'),
new SmartFileInfo(__DIR__ . '/Source/AbstractChild.php'),
];
yield [new SmartFileInfo(__DIR__ . '/FixtureExtraFiles/ExtendsAbstractChild.php.inc'), $extraFileInfos];
}
protected function getRectorClass(): string
{
return RemoveEmptyAbstractClassRector::class;
}
}

View File

@ -1,43 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassMulti
{
}
abstract class AbstractClassUsedMulti extends AbstractClassMulti
{
}
final class AnyClass2
{
public function process($some)
{
if ($some instanceof AbstractClassUsedMulti) {
}
}
}
?>
-----
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassUsedMulti
{
}
final class AnyClass2
{
public function process($some)
{
if ($some instanceof AbstractClassUsedMulti) {
}
}
}
?>

View File

@ -1,43 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassMultiUpper
{
}
abstract class AbstractClassUsedMultiUpper extends AbstractClassMultiUpper
{
}
final class AnyClass3
{
public function process($some)
{
if ($some instanceof AbstractClassMultiUpper) {
}
}
}
?>
-----
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassMultiUpper
{
}
final class AnyClass3
{
public function process($some)
{
if ($some instanceof AbstractClassMultiUpper) {
}
}
}
?>

View File

@ -1,39 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AnotherAbstractClass
{
public function getName()
{
return 'name';
}
}
abstract class SomeAbstractClass extends AnotherAbstractClass
{
}
class SomeClass extends SomeAbstractClass
{
}
?>
-----
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AnotherAbstractClass
{
public function getName()
{
return 'name';
}
}
class SomeClass extends \Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture\AnotherAbstractClass
{
}
?>

View File

@ -1,43 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AnotherAbstractClassMulti
{
public function getName()
{
return 'name';
}
}
abstract class SomeAbstractClassMulti1 extends AnotherAbstractClassMulti
{
}
abstract class SomeAbstractClassMulti2 extends SomeAbstractClassMulti1
{
}
class MultiExtends extends SomeAbstractClassMulti2
{
}
?>
-----
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AnotherAbstractClassMulti
{
public function getName()
{
return 'name';
}
}
class MultiExtends extends \Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture\AnotherAbstractClassMulti
{
}
?>

View File

@ -1,23 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SingleEmptyAbstract
{
}
class Single extends SingleEmptyAbstract
{
}
?>
-----
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
class Single
{
}
?>

View File

@ -1,21 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassUsed
{
}
class skipAbstractClassUsed extends AbstractClassUsed
{
}
final class AnyClass
{
public function process($some)
{
if ($some instanceof AbstractClassUsed) {
}
}
}

View File

@ -1,8 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SkipHasConstant
{
protected const A_CONSTANT = 'foo';
}

View File

@ -1,8 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SkipHasMethod
{
public abstract function run();
}

View File

@ -1,8 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SkipHasProperty
{
protected $foo;
}

View File

@ -1,10 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
use Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source\ATrait;
abstract class SkipHasTrait
{
use ATrait;
}

View File

@ -1,9 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
use Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source\AnInterface;
abstract class SkipImplementsInterface implements AnInterface
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
final class SkipNotAbstract
{
}

View File

@ -1,19 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SkipSingleEmptyAbstractUsed
{
}
class Single2
{
function run($some)
{
if ($some instanceof SkipSingleEmptyAbstractUsed) {
}
}
}
?>

View File

@ -1,23 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\FixtureExtraFiles;
use Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source\AbstractChild;
final class ExtendsAbstractChild extends AbstractChild
{
}
?>
-----
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\FixtureExtraFiles;
use Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source\AbstractChild;
final class ExtendsAbstractChild extends \Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source\AbstractMain
{
}
?>

View File

@ -1,7 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\FixtureExtraFiles;
abstract class SkipUsedAbstractClass
{
}

View File

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector;
use Iterator;
use Rector\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class RemoveEmptyAbstractClassRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return RemoveEmptyAbstractClassRector::class;
}
}

View File

@ -1,6 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source;
trait ATrait
{}

View File

@ -1,7 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source;
abstract class AbstractChild extends AbstractMain
{
}

View File

@ -1,10 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source;
abstract class AbstractMain
{
public function run()
{
}
}

View File

@ -1,7 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source;
interface AnInterface
{
}

View File

@ -1,12 +0,0 @@
<?php
namespace Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\Source;
use Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\FixtureExtraFiles\SkipUsedAbstractClass;
final class UseAbstract
{
public function __construct(?SkipUsedAbstractClass $class = null)
{
}
}

View File

@ -1,126 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\DeadCode\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\UseUse;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector\RemoveEmptyAbstractClassRectorTest
*/
final class RemoveEmptyAbstractClassRector extends AbstractRector
{
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Class_::class];
}
/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
return null;
}
return $this->processRemove($node);
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Empty abstract class that does nothing',
[
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass extends SomeAbstractClass
{
}
abstract class SomeAbstractClass extends AnotherAbstractClass
{
}
abstract class AnotherAbstractClass
{
public function getName()
{
return 'name';
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass extends AnotherAbstractClass
{
}
abstracst clas AnotherAbstractClass
{
public function getName()
{
return 'cowo';
}
}
CODE_SAMPLE
),
]);
}
private function shouldSkip(Class_ $class): bool
{
if (! $class->isAbstract()) {
return true;
}
if ($class->implements !== []) {
return true;
}
return $class->stmts !== [];
}
private function processRemove(Class_ $class): ?Class_
{
$className = $this->getName($class->namespacedName);
$names = $this->nodeRepository->findNames($className);
foreach ($names as $name) {
$parent = $name->getAttribute(AttributeKey::PARENT_NODE);
if ($parent instanceof Class_) {
continue;
}
if ($parent instanceof UseUse) {
continue;
}
return null;
}
$children = $this->nodeRepository->findChildrenOfClass($className);
foreach ($children as $child) {
if ($class->extends !== null) {
$parentClass = $this->getName($class->extends);
$child->extends = new FullyQualified($parentClass);
} else {
$child->extends = null;
}
}
$this->removeNode($class);
return $class;
}
}

View File

@ -131,7 +131,7 @@ CODE_SAMPLE
$arguments = [$identifierVariable, $resolvedVariable];
$staticCall = $this->nodeFactory->createStaticCall(
ResolvedConstantStaticCollector::class,
'collects',
'collect',
$arguments
);

View File

@ -1,17 +0,0 @@
<?php
declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
->public()
->autowire()
->autoconfigure();
$services->load('Rector\Utils\ProjectValidator\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/HttpKernel', __DIR__ . '/../src/ValueObject']);
};

View File

@ -1,114 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\ProjectValidator\Command;
use Rector\Core\Application\ActiveRectorsProvider;
use Rector\Core\HttpKernel\RectorKernel;
use Rector\Set\RectorSetProvider;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\HttpKernel\KernelInterface;
use Symplify\PackageBuilder\Console\ShellCode;
use Symplify\SetConfigResolver\ValueObject\Set;
use Throwable;
/**
* We'll only check one file for now.
* This makes sure that all sets are "runnable" but keeps the runtime at a managable level
*/
final class ValidateSetsCommand extends Command
{
/**
* @var string[]
*/
private const EXCLUDED_SETS = [
// required Kernel class to be set in parameters
'symfony-code-quality',
];
/**
* @var RectorSetProvider
*/
private $rectorSetProvider;
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
public function __construct(RectorSetProvider $rectorSetProvider, SymfonyStyle $symfonyStyle)
{
$this->rectorSetProvider = $rectorSetProvider;
$this->symfonyStyle = $symfonyStyle;
parent::__construct();
}
protected function configure(): void
{
$this->setDescription('[CI] Validate each sets has correct configuration by loading the configs');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$hasErrors = false;
$message = sprintf('Testing %d sets', count($this->rectorSetProvider->provide()));
$this->symfonyStyle->title($message);
foreach ($this->rectorSetProvider->provide() as $set) {
if (in_array($set->getName(), self::EXCLUDED_SETS, true)) {
continue;
}
$setFileInfo = $set->getSetFileInfo();
try {
$rectorKernel = $this->bootRectorKernelWithSet($set);
} catch (Throwable $throwable) {
$message = sprintf(
'Failed to load "%s" set from "%s" path',
$set->getName(),
$setFileInfo->getRelativeFilePathFromCwd()
);
$this->symfonyStyle->error($message);
$this->symfonyStyle->writeln($throwable->getMessage());
$hasErrors = true;
sleep(3);
continue;
}
$container = $rectorKernel->getContainer();
$activeRectorsProvider = $container->get(ActiveRectorsProvider::class);
$activeRectors = $activeRectorsProvider->provide();
$setFileInfo = $set->getSetFileInfo();
$message = sprintf(
'Set "%s" loaded correctly from "%s" path with %d rules',
$set->getName(),
$setFileInfo->getRelativeFilePathFromCwd(),
count($activeRectors)
);
$this->symfonyStyle->success($message);
}
return $hasErrors ? ShellCode::ERROR : ShellCode::SUCCESS;
}
private function bootRectorKernelWithSet(Set $set): KernelInterface
{
$rectorKernel = new RectorKernel('prod' . sha1($set->getName()), true);
$rectorKernel->setConfigs([$set->getSetPathname()]);
$rectorKernel->boot();
return $rectorKernel;
}
}