Merge pull request #2850 from rectorphp/phpstan-rector-rule

[PHPStan] Add KeepRectorNamespaceForRectorRule
This commit is contained in:
Tomas Votruba 2020-02-14 00:19:22 +01:00 committed by GitHub
commit 71a855f29d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 219 additions and 32 deletions

1
.gitignore vendored
View File

@ -13,7 +13,6 @@ create-rector.yaml
# tests - travis
/laravel-dir
/utils
# compiler
/compiler/composer.lock

View File

@ -143,7 +143,8 @@
"Rector\\NetteToSymfony\\Tests\\": "rules/nette-to-symfony/tests",
"Rector\\Nette\\Tests\\": "packages/nette/tests",
"Rector\\NodeTypeResolver\\Tests\\": "packages/node-type-resolver/tests",
"Rector\\PHPStanExtensions\\": "utils/PHPStanExtensions/src",
"Rector\\PHPStanExtensions\\": "utils/phpstan-extensions/src",
"Rector\\PHPStanExtensions\\Tests\\": "utils/phpstan-extensions/tests",
"Rector\\PHPStan\\Tests\\": "rules/phpstan/tests",
"Rector\\PHPUnitSymfony\\Tests\\": "rules/phpunit-symfony/tests",
"Rector\\PHPUnit\\Tests\\": "rules/phpunit/tests",
@ -178,9 +179,9 @@
"Rector\\DynamicTypeAnalysis\\Tests\\": "packages/dynamic-type-analysis/tests",
"Rector\\PhpDeglobalize\\Tests\\": "rules/php-deglobalize/tests",
"Rector\\Phalcon\\Tests\\": "rules/phalcon/tests",
"Rector\\Utils\\DocumentationGenerator\\": "utils/DocumentationGenerator/src",
"Rector\\Utils\\PHPStanAttributeTypeSyncer\\": "utils/PHPStanAttributeTypeSyncer/src",
"Rector\\Utils\\PHPStanStaticTypeMapperChecker\\": "utils/PHPStanStaticTypeMapperChecker/src",
"Rector\\Utils\\DocumentationGenerator\\": "utils/documentation-generator/src",
"Rector\\Utils\\PHPStanAttributeTypeSyncer\\": "utils/phpstan-attribute-type-syncer/src",
"Rector\\Utils\\PHPStanStaticTypeMapperChecker\\": "utils/phpstan-static-type-mapper-checker/src",
"Rector\\DoctrineGedmoToKnplabs\\Tests\\": "rules/doctrine-gedmo-to-knplabs/tests",
"Rector\\MinimalScope\\Tests\\": "rules/minimal-scope/tests",
"Rector\\Polyfill\\Tests\\": "packages/polyfill/tests",

View File

@ -17,9 +17,6 @@ services:
- '../src/ValueObject/*'
- '../src/functions/*'
# extra services
Rector\Symfony\Rector\Form\Helper\FormTypeStringToTypeProvider: null
Symfony\Component\Console\Application:
alias: 'Rector\Core\Console\Application'

View File

@ -93,7 +93,7 @@ parameters:
- "rules/coding-style/src/Rector/ClassMethod/NewlineBeforeNewAssignSetRector.php"
# per node logic
- 'utils/DocumentationGenerator/src/Command/DumpNodesCommand.php'
- 'utils/documentation-generator/src/Command/DumpNodesCommand.php'
# copied 3rd party logic
- 'rules/php-70/src/EregToPcreTransformer.php'
# dev

View File

@ -1,5 +1,5 @@
includes:
- 'utils/PHPStanExtensions/config/phpstan-extensions.neon'
- 'utils/phpstan-extensions/config/phpstan-extensions.neon'
- 'vendor/symplify/phpstan-extensions/config/config.neon'
- 'vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon'
- 'vendor/phpstan/phpstan/conf/bleedingEdge.neon'

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Symfony\Rector\Form\Helper;
namespace Rector\Symfony\FormHelper;
use Nette\Utils\Strings;

View File

@ -10,7 +10,7 @@ use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Symfony\Rector\Form\Helper\FormTypeStringToTypeProvider;
use Rector\Symfony\FormHelper\FormTypeStringToTypeProvider;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\AbstractTypeExtension;

View File

@ -10,7 +10,7 @@ use PhpParser\Node\Scalar\String_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Symfony\Rector\Form\Helper\FormTypeStringToTypeProvider;
use Rector\Symfony\FormHelper\FormTypeStringToTypeProvider;
use Symfony\Component\Form\FormBuilderInterface;
/**

View File

@ -6,7 +6,7 @@ sonar.projectKey=rectorphp_rector
# wildcards don't work :(
sonar.sources=compiler/src,src,rules/autodiscovery/src,rules/architecture/src,packages/attribute-aware-php-doc/src,packages/better-php-doc-parser/src,rules/cakephp/src,rules/celebrity/src,rules/code-quality/src,rules/coding-style/src,packages/console-differ/src,rules/dead-code/src,rules/doctrine/src,rules/doctrine-code-quality/src,rules/framework-migration/src,packages/file-system-rector/src,rules/elastic-search-dsl/src,rules/guzzle/src,rules/laravel/src,packages/legacy/src,rules/mysql-to-mysqli/src,rules/nette-tester-to-phpunit/src,rules/nette-to-symfony/src,packages/nette/src,packages/node-collector/src,packages/node-type-resolver/src,packages/node-name-resolver/src,rules/phpstan/src,packages/phpstan-static-type-mapper/src,rules/phpunit-symfony/src,rules/phpunit/src,rules/psr4/src,rules/php-spec-to-phpunit/src,rules/php-52/src,rules/php-53/src,rules/php-54/src,rules/php-55/src,rules/php-56/src,rules/php-70/src,rules/php-71/src,rules/php-72/src,rules/php-73/src,rules/php-74/src,rules/php-80/src,rules/removing-static/src,rules/renaming/src,rules/restoration/src,packages/refactoring/src,rules/solid/src,rules/sensio/src,rules/shopware/src,rules/silverstripe/src,packages/static-type-mapper/src,rules/sylius/src,rules/symfony-code-quality/src,rules/symfony-phpunit/src,rules/symfony/src,rules/twig/src,rules/type-declaration/src,packages/vendor-locker/src,rules/zend-to-symfony/src,packages/rector-generator/src,rules/strict-code-quality/src,packages/dynamic-type-analysis/src,rules/php-deglobalize/src,rules/phalcon/src,rules/doctrine-gedmo-to-knplabs/src,rules/minimal-scope/src,packages/polyfill/src,rules/cakephp-to-symfony/src
sonar.tests=tests,rules/autodiscovery/tests,rules/architecture/tests,packages/better-php-doc-parser/tests,rules/cakephp/tests,rules/celebrity/tests,rules/code-quality/tests,rules/coding-style/tests,rules/dead-code/tests,rules/doctrine/tests,rules/doctrine-code-quality/tests,rules/elastic-search-dsl/tests,rules/guzzle/tests,rules/laravel/tests,packages/legacy/tests,rules/mysql-to-mysqli/tests,rules/nette-tester-to-phpunit/tests,rules/nette-to-symfony/tests,packages/nette/tests,packages/node-type-resolver/tests,utils/PHPStanExtensions/src,rules/phpstan/tests,rules/phpunit-symfony/tests,rules/phpunit/tests,rules/psr4/tests,rules/php-spec-to-phpunit/tests,rules/php-52/tests,rules/php-53/tests,rules/php-54/tests,rules/php-55/tests,rules/php-56/tests,rules/php-70/tests,rules/php-71/tests,rules/php-72/tests,rules/php-73/tests,rules/php-74/tests,rules/php-80/tests,rules/removing-static/tests,rules/renaming/tests,rules/restoration/tests,rules/solid/tests,rules/sensio/tests,rules/shopware/tests,rules/silverstripe/tests,rules/sylius/tests,rules/symfony-code-quality/tests,rules/symfony-phpunit/tests,rules/symfony/tests,rules/twig/tests,rules/type-declaration/tests,rules/zend-to-symfony/tests,rules/strict-code-quality/tests,packages/dynamic-type-analysis/tests,rules/php-deglobalize/tests,rules/phalcon/tests,utils/DocumentationGenerator/src,utils/PHPStanAttributeTypeSyncer/src,utils/PHPStanStaticTypeMapperChecker/src,rules/doctrine-gedmo-to-knplabs/tests,rules/minimal-scope/tests,packages/polyfill/tests,rules/cakephp-to-symfony/tests
sonar.tests=tests,rules/autodiscovery/tests,rules/architecture/tests,packages/better-php-doc-parser/tests,rules/cakephp/tests,rules/celebrity/tests,rules/code-quality/tests,rules/coding-style/tests,rules/dead-code/tests,rules/doctrine/tests,rules/doctrine-code-quality/tests,rules/elastic-search-dsl/tests,rules/guzzle/tests,rules/laravel/tests,packages/legacy/tests,rules/mysql-to-mysqli/tests,rules/nette-tester-to-phpunit/tests,rules/nette-to-symfony/tests,packages/nette/tests,packages/node-type-resolver/tests,utils/phpstan-extensions/src,rules/phpstan/tests,rules/phpunit-symfony/tests,rules/phpunit/tests,rules/psr4/tests,rules/php-spec-to-phpunit/tests,rules/php-52/tests,rules/php-53/tests,rules/php-54/tests,rules/php-55/tests,rules/php-56/tests,rules/php-70/tests,rules/php-71/tests,rules/php-72/tests,rules/php-73/tests,rules/php-74/tests,rules/php-80/tests,rules/removing-static/tests,rules/renaming/tests,rules/restoration/tests,rules/solid/tests,rules/sensio/tests,rules/shopware/tests,rules/silverstripe/tests,rules/sylius/tests,rules/symfony-code-quality/tests,rules/symfony-phpunit/tests,rules/symfony/tests,rules/twig/tests,rules/type-declaration/tests,rules/zend-to-symfony/tests,rules/strict-code-quality/tests,packages/dynamic-type-analysis/tests,rules/php-deglobalize/tests,rules/phalcon/tests,utils/documentation-generator/src,utils/phpstan-attribute-type-syncer/src,utils/phpstan-static-type-mapper-checker/src,rules/doctrine-gedmo-to-knplabs/tests,rules/minimal-scope/tests,packages/polyfill/tests,rules/cakephp-to-symfony/tests
# see https://docs.sonarqube.org/latest/project-administration/narrowing-the-focus/#NarrowingtheFocus-patterns
sonar.exclusions=src/**/*.php.inc,rules/**/*.php.inc,packages/**/*.php.inc,packages/**/Fixture/**/*,rules/**/Fixture/**/*,tests/**/Source/**/*

View File

@ -10,7 +10,7 @@ use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\NodeTraverser\RectorNodeTraverser;
use Rector\Core\PhpParser\Parser\Parser;
use Rector\Core\PhpParser\Printer\FormatPerservingPrinter;
use Rector\Core\Rector\AffectedFilesCollector;
use Rector\Core\Report\AffectedFilesCollector;
use Rector\Core\Stubs\StubLoader;
use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider;
use Rector\NodeTypeResolver\NodeScopeAndMetadataDecorator;

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Core\Configuration\Rector\Architecture\DependencyInjection;
namespace Rector\Core\Configuration\Collector;
use PHPStan\Type\Type;

View File

@ -12,7 +12,7 @@ use PhpParser\NodeTraverser;
use Rector\Core\Contract\PhpParser\Node\CommanderInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\NodeVisitorFactory\NodeRemovingNodeVisitorFactory;
use Rector\Core\Rector\AffectedFilesCollector;
use Rector\Core\Report\AffectedFilesCollector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\SmartFileSystem\SmartFileInfo;

View File

@ -10,7 +10,7 @@ use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ObjectType;
use Rector\Core\Configuration\Rector\Architecture\DependencyInjection\VariablesToPropertyFetchCollection;
use Rector\Core\Configuration\Collector\VariablesToPropertyFetchCollection;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;

View File

@ -9,7 +9,7 @@ use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ObjectType;
use Rector\Core\Configuration\Rector\Architecture\DependencyInjection\VariablesToPropertyFetchCollection;
use Rector\Core\Configuration\Collector\VariablesToPropertyFetchCollection;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Core\Rector;
namespace Rector\Core\Report;
use Symplify\SmartFileSystem\SmartFileInfo;

View File

@ -1,5 +1,6 @@
services:
- { class: Rector\PHPStanExtensions\Rule\ClassMethod\PreventParentMethodVisibilityOverrideRule, tags: [phpstan.rules.rule] }
- { class: Rector\PHPStanExtensions\Rule\ClassLike\KeepRectorNamespaceForRectorRule, tags: [phpstan.rules.rule] }
- Rector\PHPStanExtensions\Utils\PHPStanValueResolver

View File

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Rule\ClassLike;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
final class KeepRectorNamespaceForRectorRule implements Rule
{
public function getNodeType(): string
{
return ClassLike::class;
}
/**
* @param ClassLike $node
* @return RuleError[]
*/
public function processNode(Node $node, Scope $scope): array
{
if ($this->shouldSkip($node, $scope)) {
return [];
}
/** @var string $classLikeName */
$classLikeName = $node->name->toString();
$ruleError = $this->createRuleError($node, $scope, $classLikeName);
return [$ruleError];
}
private function shouldSkip(Node $node, Scope $scope): bool
{
$namespace = $scope->getNamespace();
if ($namespace === null) {
return true;
}
// skip interface and tests
if (Strings::match($namespace, '#\\\\(Contract|Exception|Tests)\\\\#')) {
return true;
}
if (! Strings::endsWith($namespace, '\\Rector') && ! Strings::match($namespace, '#\\\\Rector\\\\#')) {
return true;
}
$name = $node->name;
if ($name === null) {
return true;
}
// correct name
$classLikeName = $name->toString();
return (bool) Strings::match($classLikeName, '#(Rector|Test|Trait)$#');
}
private function createRuleError(Node $node, Scope $scope, string $classLikeName): RuleError
{
$message = sprintf(
'Change namespace for "%s". It cannot be in "Rector" namespace, unless Rector rule.',
$classLikeName
);
$ruleErrorBuilder = RuleErrorBuilder::message($message);
$ruleErrorBuilder->line($node->getLine());
$ruleErrorBuilder->file($scope->getFile());
return $ruleErrorBuilder->build();
}
}

View File

@ -9,8 +9,8 @@ use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrors\RuleErrorWithMessageAndLineAndFile;
use Rector\Exception\NotImplementedException;
use PHPStan\Rules\RuleErrorBuilder;
use Rector\Core\Exception\NotImplementedException;
use ReflectionMethod;
final class PreventParentMethodVisibilityOverrideRule implements Rule
@ -49,16 +49,7 @@ final class PreventParentMethodVisibilityOverrideRule implements Rule
$methodVisibility = $this->resolveReflectionMethodVisibilityAsStrings($parentReflectionMethod);
$ruleError = new RuleErrorWithMessageAndLineAndFile(
sprintf(
'Change "%s()" method visibility to "%s" to respect parent method visibility.',
$methodName,
$methodVisibility
),
$node->getLine(),
$scope->getFile()
);
$ruleError = $this->createRuleError($node, $scope, $methodName, $methodVisibility);
return [$ruleError];
}
@ -100,4 +91,19 @@ final class PreventParentMethodVisibilityOverrideRule implements Rule
throw new NotImplementedException();
}
private function createRuleError(Node $node, Scope $scope, string $methodName, string $methodVisibility): RuleError
{
$message = sprintf(
'Change "%s()" method visibility to "%s" to respect parent method visibility.',
$methodName,
$methodVisibility
);
$ruleErrorBuilder = RuleErrorBuilder::message($message);
$ruleErrorBuilder->line($node->getLine());
$ruleErrorBuilder->file($scope->getFile());
return $ruleErrorBuilder->build();
}
}

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\ClassLike;
use Iterator;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use Rector\PHPStanExtensions\Rule\ClassLike\KeepRectorNamespaceForRectorRule;
final class KeepRectorNamespaceForRectorRuleTest extends RuleTestCase
{
/**
* @param array<string|int> $expectedErrorsWithLines
* @dataProvider provideData()
*/
public function testRule(string $filePath, array $expectedErrorsWithLines): void
{
$this->analyse([$filePath], [$expectedErrorsWithLines]);
}
public function provideData(): Iterator
{
yield [__DIR__ . '/Source/Rector/ClassInCorrectNamespaceRector.php', []];
yield [
__DIR__ . '/Source/Rector/WrongClass.php',
['Change namespace for "WrongClass". It cannot be in Rector namespace, unless Rector rule.', 8],
];
}
protected function getRule(): Rule
{
return new KeepRectorNamespaceForRectorRule();
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\ClassLike\Source\Rector;
final class ClassInCorrectNamespaceRector
{
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\ClassLike\Source\Rector;
final class WrongClass
{
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\ClassMethod;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use Rector\PHPStanExtensions\Rule\ClassMethod\PreventParentMethodVisibilityOverrideRule;
final class PreventParentMethodVisibilityOverrideRuleTest extends RuleTestCase
{
public function testRule(): void
{
$this->analyse(
[__DIR__ . '/Source/ClassWithOverridingVisibility.php'],
[['Change "run()" method visibility to "protected" to respect parent method visibility.', 10]]
);
}
protected function getRule(): Rule
{
return new PreventParentMethodVisibilityOverrideRule();
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\ClassMethod\Source;
final class ClassWithOverridingVisibility extends GoodVisibility
{
public function run()
{
}
}
abstract class GoodVisibility
{
protected function run()
{
}
}