[Symplify 9] Follow up + phpstan rules tidying (#4625)

* improve static

* improve phpstan rules

* [PHPStanExtensions] Drop CheckCodeSampleBeforeAfterAlwaysDifferentRule, already part of domain in CodeSample

* cleanup
This commit is contained in:
Tomas Votruba 2020-11-16 23:32:24 +00:00 committed by GitHub
parent 3b1e31ef8c
commit 661b2f0a65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 334 additions and 323 deletions

View File

@ -17,10 +17,6 @@ Let's say we want to **change method calls from `set*` to `change*`**.
Create a class that extends [`Rector\Core\Rector\AbstractRector`](/src/Rector/AbstractRector.php). It will inherit useful methods e.g. to check node type and name. See the source (or type `$this->` in an IDE) for a list of available methods.
```php
<?php
declare(strict_types=1);
namespace Utils\Rector;
use Nette\Utils\Strings;
@ -29,7 +25,7 @@ use PhpParser\Node\Identifier;
use PhpParser\Node\Expr\MethodCall;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class MyFirstRector extends AbstractRector
{
@ -67,11 +63,11 @@ final class MyFirstRector extends AbstractRector
/**
* From this method documentation is generated.
*/
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition(
return new RuleDefinition(
'Change method calls from set* to change*.', [
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample(
new CodeSample(
// code before
'$user->setPassword("123456");',
// code after

View File

@ -39,7 +39,8 @@ final class TagValueNodeReprintTest extends AbstractPhpDocInfoTest
public function provideData(): Iterator
{
foreach ($this->getDirectoriesByTagValueNodes() as $tagValueNode => $directory) {
foreach ($this->findFilesFromDirectory($directory) as $fileInfos) {
$filesInDirectory = $this->findFilesFromDirectory($directory);
foreach ($filesInDirectory as $fileInfos) {
foreach ($fileInfos as $fileInfo) {
yield [$fileInfo, $tagValueNode];
}

View File

@ -49,8 +49,10 @@ final class DependencyResolver
$analysedFileAbsolutesPaths[] = $analysedFile->getRealPath();
}
$dependencies = [];
foreach ($this->phpStanDependencyResolver->resolveDependencies($node, $scope) as $nodeDependency) {
$dependencyFiles = [];
$nodeDependencies = $this->phpStanDependencyResolver->resolveDependencies($node, $scope);
foreach ($nodeDependencies as $nodeDependency) {
$dependencyFile = $nodeDependency->getFileName();
if (! $dependencyFile) {
continue;
@ -65,11 +67,11 @@ final class DependencyResolver
continue;
}
$dependencies[] = $dependencyFile;
$dependencyFiles[] = $dependencyFile;
}
$dependencies = array_unique($dependencies, SORT_STRING);
$dependencyFiles = array_unique($dependencyFiles, SORT_STRING);
return array_values($dependencies);
return array_values($dependencyFiles);
}
}

View File

@ -65,8 +65,9 @@ final class PregMatchTypeCorrector
return $originalType;
}
foreach ($this->getVariableUsages($node) as $usage) {
$possiblyArg = $usage->getAttribute(AttributeKey::PARENT_NODE);
$variableUsages = $this->getVariableUsages($node);
foreach ($variableUsages as $variableUsage) {
$possiblyArg = $variableUsage->getAttribute(AttributeKey::PARENT_NODE);
if (! $possiblyArg instanceof Arg) {
continue;
}

View File

@ -198,7 +198,8 @@ final class PHPStanNodeScopeResolver
}
try {
foreach ($this->dependencyResolver->resolveDependencies($node, $scope) as $dependentFile) {
$dependentFiles = $this->dependencyResolver->resolveDependencies($node, $scope);
foreach ($dependentFiles as $dependentFile) {
$this->dependentFiles[] = $dependentFile;
}
} catch (AnalysedCodeException $analysedCodeException) {

View File

@ -7,8 +7,8 @@ namespace Rector\__Package__\Rector\__Category__;
use PhpParser\Node;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
__Resources__
@ -20,10 +20,10 @@ final class __Name__ extends AbstractRector implements ConfigurableRectorInterfa
__ConfigurationProperties__
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('__Description__', [
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample(
return new RuleDefinition('__Description__', [
new ConfiguredCodeSample(
__CodeBeforeExample__,
__CodeAfterExample__,
__RuleConfiguration__

View File

@ -7,8 +7,8 @@ namespace Rector\ModeratePackage\Rector\MethodCall;
use PhpParser\Node;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -26,10 +26,10 @@ public const CLASS_TYPE_TO_METHOD_NAME = 'class_type_to_method_name';
*/
private $classTypeToMethodName = [];
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('Change $service->arg(...) to $service->call(...)', [
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample(
return new RuleDefinition('Change $service->arg(...) to $service->call(...)', [
new ConfiguredCodeSample(
<<<'PHP'
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

View File

@ -7,8 +7,8 @@ namespace Utils\Rector\Rector\MethodCall;
use PhpParser\Node;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -26,10 +26,10 @@ public const CLASS_TYPE_TO_METHOD_NAME = 'class_type_to_method_name';
*/
private $classTypeToMethodName = [];
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('Change $service->arg(...) to $service->call(...)', [
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample(
return new RuleDefinition('Change $service->arg(...) to $service->call(...)', [
new ConfiguredCodeSample(
<<<'PHP'
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

View File

@ -26,10 +26,10 @@ final class RunnableClassFinder
*/
private $nodeFinder;
public function __construct()
public function __construct(NodeFinder $nodeFinder)
{
$this->parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
$this->nodeFinder = new NodeFinder();
$this->nodeFinder = $nodeFinder;
}
public function find(string $content): string

View File

@ -6,6 +6,7 @@ namespace Rector\Testing\PHPUnit;
use Nette\Utils\Random;
use Nette\Utils\Strings;
use PhpParser\NodeFinder;
use Rector\Testing\Contract\RunnableInterface;
use Rector\Testing\PHPUnit\Runnable\ClassLikeNamesSuffixer;
use Rector\Testing\PHPUnit\Runnable\RunnableClassFinder;
@ -31,7 +32,7 @@ final class RunnableRectorFactory
public function __construct()
{
$this->runnableClassFinder = new RunnableClassFinder();
$this->runnableClassFinder = new RunnableClassFinder(new NodeFinder());
$this->classLikeNamesSuffixer = new ClassLikeNamesSuffixer();
$this->smartFileSystem = new SmartFileSystem();
}

View File

@ -93,7 +93,6 @@ services:
# 'phpDocumentor\Reflection\DocBlock\Tags\Return_': 'PhpParser\Node\Stmt\Return_'
# 'Closure': 'PhpParser\Node\Expr\Closure'
# 'PHPUnit\TextUI\Configuration\Variable': 'PhpParser\Node\Expr\Variable'
# 'PhpCsFixer\FixerDefinition\CodeSample': 'Rector\Core\RectorDefinition\CodeSample'
# 'SebastianBergmann\Type\MixedType': 'PHPStan\Type\MixedType'
# 'Hoa\Protocol\Node\Node': 'PhpParser\Node'
# 'Nette\Utils\FileSystem': 'Symplify\SmartFileSystem\SmartFileSystem'
@ -721,15 +720,12 @@ parameters:
# symplify 9
- '#Use decoupled factory service to create "(.*?)" object#'
- '#Use "\$class\-\>namespaceName" instead of "\$class\-\>name" that only returns short class name#'
- '#Method call on new expression is not allowed#'
- '#Constant string value need to only have small letters, _, \-, \. and numbers#'
- '#Method nor static call in if \(\) or elseif \(\) is not allowed\. Extract expression to a new variable assign on line before#'
- '#Use another value object over array with string\-keys and objects, array<string, ValueObject\>#'
- '#SymfonyStyle usage is unneeded for only newline, write, and/or writeln, use PHP_EOL and concatenation instead#'
- '#Method nor static call in foreach is not allowed\. Extract expression to a new variable assign on line before#'
- '#Trait method "(.*?)" should not contain any logic, but only delegate to other class call#'
- '#Do not use factory/method call in constructor\. Put factory in config and get service with dependency injection#'
- '#Property with protected modifier is not allowed\. Use interface contract method instead#'
- '#Constant string value need to only have small letters, _, \-, \. and numbers#'
-
message: '#Use explicit names over dynamic ones#'

View File

@ -76,7 +76,8 @@ CODE_SAMPLE
$this->processSingleQuoted($node, $doubleQuoteCount, $singleQuoteCount);
}
if ($node->getAttribute(AttributeKey::KIND) === String_::KIND_DOUBLE_QUOTED) {
$quoteKind = $node->getAttribute(AttributeKey::KIND);
if ($quoteKind === String_::KIND_DOUBLE_QUOTED) {
$this->processDoubleQuoted($node, $singleQuoteCount, $doubleQuoteCount);
}

View File

@ -46,7 +46,8 @@ final class LivingCodeManipulator
public function addLivingCodeBeforeNode(Expr $expr, Node $addBeforeThisNode): void
{
foreach ($this->keepLivingCodeFromExpr($expr) as $expr) {
$livinExprs = $this->keepLivingCodeFromExpr($expr);
foreach ($livinExprs as $expr) {
$this->nodesToAddCollector->addNodeBeforeNode(new Expression($expr), $addBeforeThisNode);
}
}

View File

@ -141,9 +141,9 @@ CODE_SAMPLE
}
// differnt class, probably inheritance
if ($methodCall->getAttribute(AttributeKey::CLASS_NAME) !== $classMethod->getAttribute(
AttributeKey::CLASS_NAME
)) {
$methodCallClassName = $methodCall->getAttribute(AttributeKey::CLASS_NAME);
$classMethodClassName = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
if ($methodCallClassName !== $classMethodClassName) {
return true;
}

View File

@ -93,7 +93,9 @@ final class FormTypeStringToTypeProvider
}
$serviceMap = $this->serviceMapProvider->provide();
foreach ($serviceMap->getServicesByTag('form.type') as $formTypeServiceDefinition) {
$formTypeServiceDefinitions = $serviceMap->getServicesByTag('form.type');
foreach ($formTypeServiceDefinitions as $formTypeServiceDefinition) {
$formTypeTag = $formTypeServiceDefinition->getTag('form.type');
if ($formTypeTag === null) {
continue;

View File

@ -117,7 +117,8 @@ CODE_SAMPLE
}
// is there return without nesting?
if ($this->areNodesEqual($node->getAttribute(AttributeKey::PARENT_NODE), $classMethod)) {
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
if ($this->areNodesEqual($parentNode, $classMethod)) {
$hasReturn = true;
}

View File

@ -83,6 +83,8 @@ final class ConstantArrayTypeToCallReflectionResolver implements TypeToCallRefle
return ConstantArrayTypeAndMethod::createUnknown();
}
$objectWithoutClassType = new ObjectWithoutClassType();
if ($classOrObjectType instanceof ConstantStringType) {
$value = $classOrObjectType->getValue();
if (! $this->reflectionProvider->hasClass($value)) {
@ -91,7 +93,7 @@ final class ConstantArrayTypeToCallReflectionResolver implements TypeToCallRefle
$classReflection = $this->reflectionProvider->getClass($value);
$type = new ObjectType($classReflection->getName());
} elseif ((new ObjectWithoutClassType())->isSuperTypeOf($classOrObjectType)->yes()) {
} elseif ($objectWithoutClassType->isSuperTypeOf($classOrObjectType)->yes()) {
$type = $classOrObjectType;
} else {
return ConstantArrayTypeAndMethod::createUnknown();

View File

@ -67,7 +67,9 @@ final class ClassConstManipulator
}
$searchInNodes = [$classLike];
foreach ($this->classManipulator->getUsedTraits($classLike) as $name) {
$usedTraitNames = $this->classManipulator->getUsedTraits($classLike);
foreach ($usedTraitNames as $name) {
$name = $this->parsedNodeCollector->findTrait((string) $name);
if ($name === null) {
continue;

View File

@ -260,7 +260,9 @@ final class IfManipulator
return [];
}
if ($this->betterNodeFinder->findInstanceOf($currentIf->stmts, Exit_::class) !== []) {
/** @var Exit_[] $exits */
$exits = $this->betterNodeFinder->findInstanceOf($currentIf->stmts, Exit_::class);
if ($exits !== []) {
return [];
}

View File

@ -167,7 +167,9 @@ final class PropertyManipulator
public function isPropertyChangeable(Property $property): bool
{
foreach ($this->getPrivatePropertyFetches($property) as $propertyFetch) {
$propertyFetches = $this->getPrivatePropertyFetches($property);
foreach ($propertyFetches as $propertyFetch) {
if ($this->isChangeableContext($propertyFetch)) {
return true;
}

View File

@ -454,12 +454,12 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
}
$fileInfo = $originalNode->getAttribute(AttributeKey::FILE_INFO);
$originalParent = $originalNode->getAttribute(AttributeKey::PARENT_NODE);
if ($fileInfo !== null) {
$node->setAttribute(AttributeKey::FILE_INFO, $originalNode->getAttribute(AttributeKey::FILE_INFO));
} elseif ($originalNode->getAttribute(AttributeKey::PARENT_NODE) !== null) {
/** @var Node $parentOriginalNode */
$parentOriginalNode = $originalNode->getAttribute(AttributeKey::PARENT_NODE);
$node->setAttribute(AttributeKey::FILE_INFO, $parentOriginalNode->getAttribute(AttributeKey::FILE_INFO));
} elseif ($originalParent instanceof Node) {
$node->setAttribute(AttributeKey::FILE_INFO, $originalParent->getAttribute(AttributeKey::FILE_INFO));
}
}

View File

@ -11,7 +11,6 @@ use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Property;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\Manipulator\PropertyManipulator;
@ -204,13 +203,6 @@ trait ComplexRemovalTrait
}
}
private function addLivingCodeBeforeNode(Expr $expr, Node $addBeforeThisNode): void
{
foreach ($this->livingCodeManipulator->keepLivingCodeFromExpr($expr) as $expr) {
$this->addNodeBeforeNode(new Expression($expr), $addBeforeThisNode);
}
}
private function isExpressionVariableNotAssign(Node $node): bool
{
if ($node !== null) {

View File

@ -5,11 +5,11 @@ declare(strict_types=1);
namespace Rector\Core\Tests\Configuration\Source;
use Rector\Core\Contract\Rector\RectorInterface;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class CustomLocalRector implements RectorInterface
{
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
// TODO: Implement getDefinition() method.
}

View File

@ -1,32 +1,7 @@
includes:
- rector-rules.neon
services:
-
class: Rector\PHPStanExtensions\Rule\RequireRectorCategoryByGetNodeTypesRule
tags: [phpstan.rules.rule]
-
class: Symplify\PHPStanRules\Rules\PreventParentMethodVisibilityOverrideRule
tags: [phpstan.rules.rule]
-
class: Rector\PHPStanExtensions\Rule\KeepRectorNamespaceForRectorRule
tags: [phpstan.rules.rule]
-
class: Rector\PHPStanExtensions\Rule\ConfigurableRectorRule
tags: [phpstan.rules.rule]
-
class: Rector\PHPStanExtensions\Rule\CheckGetNodeTypesReturnPhpParserNodeRule
tags: [phpstan.rules.rule]
-
class: Rector\PHPStanExtensions\Rule\RectorRuleAndValueObjectHaveSameStartsRule
tags: [phpstan.rules.rule]
-
class: Rector\PHPStanExtensions\Rule\CheckCodeSampleBeforeAfterAlwaysDifferentRule
tags: [phpstan.rules.rule]
- Rector\PHPStanExtensions\Utils\PHPStanValueResolver
# $node->getAttribute($1) => Type|null by $1

View File

@ -0,0 +1,20 @@
services:
-
class: Rector\PHPStanExtensions\Rule\RequireRectorCategoryByGetNodeTypesRule
tags: [phpstan.rules.rule]
-
class: Symplify\PHPStanRules\Rules\PreventParentMethodVisibilityOverrideRule
tags: [phpstan.rules.rule]
-
class: Rector\PHPStanExtensions\Rule\KeepRectorNamespaceForRectorRule
tags: [phpstan.rules.rule]
-
class: Rector\PHPStanExtensions\Rule\ConfigurableRectorRule
tags: [phpstan.rules.rule]
-
class: Rector\PHPStanExtensions\Rule\CheckGetNodeTypesReturnPhpParserNodeRule
tags: [phpstan.rules.rule]

View File

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\NodeFinder;
use Symplify\PHPStanRules\Naming\SimpleNameResolver;
use Symplify\PHPStanRules\ValueObject\PHPStanAttributeKey;
final class SymfonyConfigRectorValueObjectResolver
{
/**
* @var string
*/
private const INLINE_FUNCTION_NAME = 'Rector\SymfonyPhpConfig\inline_value_objects';
/**
* @var NodeFinder
*/
private $nodeFinder;
/**
* @var SimpleNameResolver
*/
private $simpleNameResolver;
public function __construct(NodeFinder $nodeFinder, SimpleNameResolver $simpleNameResolver)
{
$this->nodeFinder = $nodeFinder;
$this->simpleNameResolver = $simpleNameResolver;
}
public function resolveFromSetMethodCall(MethodCall $methodCall): ?string
{
$parent = $methodCall->getAttribute(PHPStanAttributeKey::PARENT);
while (! $parent instanceof Expression) {
$parent = $parent->getAttribute(PHPStanAttributeKey::PARENT);
}
/** @var FuncCall|null $inlineFuncCall */
$inlineFuncCall = $this->nodeFinder->findFirst($parent, function (Node $node): bool {
if (! $node instanceof FuncCall) {
return false;
}
return $this->simpleNameResolver->isName($node->name, self::INLINE_FUNCTION_NAME);
});
if ($inlineFuncCall === null) {
return null;
}
/** @var New_|null $new */
$new = $this->nodeFinder->findFirstInstanceOf($inlineFuncCall, New_::class);
if ($new === null) {
return null;
}
return $this->simpleNameResolver->getName($new->class);
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\NodeAnalyzer;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Type\TypeWithClassName;
use Symplify\PHPStanRules\Naming\SimpleNameResolver;
final class TypeAndNameAnalyzer
{
/**
* @var SimpleNameResolver
*/
private $simpleNameResolver;
public function __construct(SimpleNameResolver $simpleNameResolver)
{
$this->simpleNameResolver = $simpleNameResolver;
}
public function isMethodCallTypeAndName(
MethodCall $methodCall,
Scope $scope,
string $desiredClassType,
string $desiredMethodName
): bool {
$callerType = $scope->getType($methodCall->var);
if (! $callerType instanceof TypeWithClassName) {
return false;
}
if (! is_a($callerType->getClassName(), $desiredClassType, true)) {
return false;
}
return $this->simpleNameResolver->isName($methodCall->name, $desiredMethodName);
}
}

View File

@ -1,58 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Rule;
use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
/**
* @see \Rector\PHPStanExtensions\Tests\Rule\CheckCodeSampleBeforeAfterAlwaysDifferentRule\CheckCodeSampleBeforeAfterAlwaysDifferentRuleTest
*/
final class CheckCodeSampleBeforeAfterAlwaysDifferentRule implements Rule
{
/**
* @var string
*/
public const ERROR_MESSAGE = 'Code sample before and after must be different';
public function getNodeType(): string
{
return New_::class;
}
/**
* @param New_ $node
* @return string[]
*/
public function processNode(Node $node, Scope $scope): array
{
$class = $node->class;
if (! $class instanceof FullyQualified) {
return [];
}
$className = $class->toString();
if ($className !== CodeSample::class) {
return [];
}
$args = $node->args;
/** @var String_ $firstParameter */
$firstParameter = $args[0]->value;
/** @var String_ $secondParameter */
$secondParameter = $args[1]->value;
if ($firstParameter->value !== $secondParameter->value) {
return [];
}
return [self::ERROR_MESSAGE];
}
}

View File

@ -15,6 +15,7 @@ use PHPStan\Rules\Rule;
use PHPStan\ShouldNotHappenException;
/**
* @todo make configurable and part of Symplify
* @see \Rector\PHPStanExtensions\Tests\Rule\CheckGetNodeTypesReturnPhpParserNodeRule\CheckGetNodeTypesReturnPhpParserNodeRuleTest
*/
final class CheckGetNodeTypesReturnPhpParserNodeRule implements Rule
@ -22,7 +23,7 @@ final class CheckGetNodeTypesReturnPhpParserNodeRule implements Rule
/**
* @var string
*/
public const ERROR = "%s::getNodeTypes() returns class names that are not instances of %s:\n%s";
public const ERROR_MESSAGE = "%s::getNodeTypes() returns class names that are not instances of %s:\n%s";
public function getNodeType(): string
{
@ -51,7 +52,7 @@ final class CheckGetNodeTypesReturnPhpParserNodeRule implements Rule
$classReflection = $scope->getClassReflection();
$errorMessage = sprintf(
self::ERROR,
self::ERROR_MESSAGE,
$classReflection->getName(),
Node::class,
implode(",\n", $incorrectClassNames)

View File

@ -112,6 +112,7 @@ final class ConfigurableRectorRule implements Rule
return null;
});
return $nodes !== [];
}
}

View File

@ -7,15 +7,12 @@ namespace Rector\PHPStanExtensions\Rule;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use PhpParser\NodeFinder;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use Rector\PHPStanExtensions\NodeAnalyzer\SymfonyConfigRectorValueObjectResolver;
use Rector\PHPStanExtensions\NodeAnalyzer\TypeAndNameAnalyzer;
use Symplify\PHPStanRules\Naming\SimpleNameResolver;
/**
* @see \Rector\PHPStanExtensions\Tests\Rule\RectorRuleAndValueObjectHaveSameStartsRule\RectorRuleAndValueObjectHaveSameStartsRuleTest
@ -25,7 +22,7 @@ final class RectorRuleAndValueObjectHaveSameStartsRule implements Rule
/**
* @var string
*/
public const ERROR = 'Value Object class name "%s" is incorrect. The correct class name is "%s".';
public const ERROR_MESSAGE = 'Change "%s" name to "%s", so it respects the Rector rule name';
/**
* @var string
@ -33,6 +30,31 @@ final class RectorRuleAndValueObjectHaveSameStartsRule implements Rule
*/
private const RECTOR_SUFFIX_REGEX = '#Rector$#';
/**
* @var SimpleNameResolver
*/
private $simpleNameResolver;
/**
* @var TypeAndNameAnalyzer
*/
private $typeAndNameAnalyzer;
/**
* @var SymfonyConfigRectorValueObjectResolver
*/
private $symfonyConfigRectorValueObjectResolver;
public function __construct(
SimpleNameResolver $simpleNameResolver,
TypeAndNameAnalyzer $typeAndNameAnalyzer,
SymfonyConfigRectorValueObjectResolver $symfonyConfigRectorValueObjectResolver
) {
$this->simpleNameResolver = $simpleNameResolver;
$this->typeAndNameAnalyzer = $typeAndNameAnalyzer;
$this->symfonyConfigRectorValueObjectResolver = $symfonyConfigRectorValueObjectResolver;
}
public function getNodeType(): string
{
return MethodCall::class;
@ -44,108 +66,71 @@ final class RectorRuleAndValueObjectHaveSameStartsRule implements Rule
*/
public function processNode(Node $node, Scope $scope): array
{
if ($this->shouldSkip($node)) {
if ($this->shouldSkip($node, $scope)) {
return [];
}
$valueObjectClassName = $this->getValueObjectClassName($node);
if ($valueObjectClassName === null) {
$rectorShortClass = $this->resolveRectorShortClass($node);
if ($rectorShortClass === null) {
return [];
}
$rectorRuleClassName = $this->getRectorRuleClassName($node);
if ($rectorRuleClassName === null) {
$valueObjectShortClass = $this->resolveValueObjectShortClass($node);
if ($valueObjectShortClass === null) {
return [];
}
if ($rectorRuleClassName === $valueObjectClassName . 'Rector') {
$expectedValueObjectShortClass = Strings::replace($rectorShortClass, self::RECTOR_SUFFIX_REGEX, '');
if ($expectedValueObjectShortClass === $valueObjectShortClass) {
return [];
}
return [sprintf(
self::ERROR,
$valueObjectClassName,
// @see https://regex101.com/r/F8z9PY/1
Strings::replace($rectorRuleClassName, self::RECTOR_SUFFIX_REGEX, '')
)];
$errorMessage = sprintf(self::ERROR_MESSAGE, $valueObjectShortClass, $expectedValueObjectShortClass);
return [$errorMessage];
}
private function shouldSkip(MethodCall $methodCall): bool
private function shouldSkip(MethodCall $methodCall, Scope $scope): bool
{
/** @var Identifier $name */
$name = $methodCall->name;
if ($name->toString() !== 'call') {
return true;
}
/** @var String_ $expr */
$expr = $methodCall->args[0]->value;
return $expr->value !== 'configure';
return ! $this->typeAndNameAnalyzer->isMethodCallTypeAndName(
$methodCall,
$scope,
'Symfony\Component\DependencyInjection\Loader\Configurator\ServicesConfigurator', 'set'
);
}
private function getValueObjectClassName(MethodCall $methodCall): ?string
private function resolveShortClass(string $class): string
{
$nodeFinder = new NodeFinder();
$inlineValueObjectsNode = $nodeFinder->findFirst($methodCall->args[1], function (Node $node): ?FuncCall {
if (! $node instanceof FuncCall) {
return null;
}
$className = $this->getClassNameFromNode($node);
if ($className === null) {
return null;
}
return (string) Strings::after($class, '\\', -1);
}
if ($className !== 'inline_value_objects') {
return null;
}
return $node;
});
if ($inlineValueObjectsNode === null) {
private function resolveRectorShortClass(MethodCall $methodCall): ?string
{
$setFirstArgValue = $methodCall->args[0]->value;
if (! $setFirstArgValue instanceof ClassConstFetch) {
return null;
}
$valueObjectNode = $nodeFinder->findFirstInstanceOf($inlineValueObjectsNode, New_::class);
if ($valueObjectNode === null) {
$rectorClass = $this->simpleNameResolver->getName($setFirstArgValue->class);
if ($rectorClass === null) {
return null;
}
/** @var FullyQualified $classNode */
$classNode = $valueObjectNode->class;
if (class_implements($classNode->toCodeString()) !== []) {
return null;
}
return $this->getClassNameFromNode($valueObjectNode);
return $this->resolveShortClass($rectorClass);
}
private function getRectorRuleClassName(Node $node): ?string
private function resolveValueObjectShortClass(MethodCall $methodCall): ?string
{
/** @var ClassConstFetch $classConstFetch */
$classConstFetch = $node->var->args[0]->value;
return $this->getClassNameFromNode($classConstFetch);
}
private function getClassNameFromNode(Node $node): ?string
{
$parts = [];
if ($node instanceof New_ || $node instanceof ClassConstFetch) {
/** @var FullyQualified $classNode */
$classNode = $node->class;
$parts = $classNode->parts;
}
if ($node instanceof FuncCall) {
/** @var FullyQualified $nameNode */
$nameNode = $node->name;
$parts = $nameNode->parts;
}
$className = end($parts);
if ($className === false) {
$valueObjectClass = $this->symfonyConfigRectorValueObjectResolver->resolveFromSetMethodCall($methodCall);
if ($valueObjectClass === null) {
return null;
}
return $className;
// is it implements interface, it can have many forms
if (class_implements($valueObjectClass) !== []) {
return null;
}
return $this->resolveShortClass($valueObjectClass);
}
}

View File

@ -1,39 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\CheckCodeSampleBeforeAfterAlwaysDifferentRule;
use Iterator;
use PHPStan\Rules\Rule;
use Rector\PHPStanExtensions\Rule\CheckCodeSampleBeforeAfterAlwaysDifferentRule;
use Symplify\PHPStanExtensions\Testing\AbstractServiceAwareRuleTestCase;
final class CheckCodeSampleBeforeAfterAlwaysDifferentRuleTest extends AbstractServiceAwareRuleTestCase
{
/**
* @dataProvider provideData()
* @param array<string|string[]|int[]> $expectedErrorsWithLines
*/
public function testRule(string $filePath, array $expectedErrorsWithLines): void
{
$this->analyse([$filePath], $expectedErrorsWithLines);
}
public function provideData(): Iterator
{
yield [__DIR__ . '/Fixture/BeforeAfterDifferent.php', []];
yield [
__DIR__ . '/Fixture/BeforeAfterSame.php',
[[CheckCodeSampleBeforeAfterAlwaysDifferentRule::ERROR_MESSAGE, 9]],
];
}
protected function getRule(): Rule
{
return $this->getRuleFromConfig(
CheckCodeSampleBeforeAfterAlwaysDifferentRule::class,
__DIR__ . '/../../../config/phpstan-extensions.neon'
);
}
}

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\CheckCodeSampleBeforeAfterAlwaysDifferentRule\Fixture;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample('A', 'B');

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\CheckCodeSampleBeforeAfterAlwaysDifferentRule\Fixture;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample('A', 'A');

View File

@ -30,7 +30,7 @@ final class CheckGetNodeTypesReturnPhpParserNodeRuleTest extends AbstractService
yield [__DIR__ . '/Fixture/SkipInterface.php', []];
$errorMessage = sprintf(
CheckGetNodeTypesReturnPhpParserNodeRule::ERROR,
CheckGetNodeTypesReturnPhpParserNodeRule::ERROR_MESSAGE,
IncorrectReturnRector::class,
Node::class,
ClassNotOfPhpParserNode::class
@ -42,7 +42,7 @@ final class CheckGetNodeTypesReturnPhpParserNodeRuleTest extends AbstractService
{
return $this->getRuleFromConfig(
CheckGetNodeTypesReturnPhpParserNodeRule::class,
__DIR__ . '/../../../config/phpstan-extensions.neon'
__DIR__ . '/config/configured_rule.neon'
);
}
}

View File

@ -0,0 +1,6 @@
services:
-
class: Rector\PHPStanExtensions\Rule\CheckGetNodeTypesReturnPhpParserNodeRule
tags: [phpstan.rules.rule]
- Symplify\PHPStanRules\Naming\SimpleNameResolver

View File

@ -5,8 +5,8 @@ declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\ConfigurableRectorRule\Fixture;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Rector\Transform\ValueObject\StaticCallToFuncCall;
final class ImplementsAndHasConfiguredCodeSampleRector implements ConfigurableRectorInterface
@ -21,10 +21,10 @@ final class ImplementsAndHasConfiguredCodeSampleRector implements ConfigurableRe
// TODO: Implement configure() method.
}
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('Turns static call to function call.', [
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample(
return new RuleDefinition('Turns static call to function call.', [
new ConfiguredCodeSample(
'OldClass::oldMethod("args");',
'new_function("args");',
[

View File

@ -6,8 +6,8 @@ namespace Rector\PHPStanExtensions\Tests\Rule\ConfigurableRectorRule\Fixture;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Rector\Transform\ValueObject\StaticCallToFuncCall;
final class ImplementsAndHasNoConfiguredCodeSampleRector implements ConfigurableRectorInterface
@ -17,9 +17,9 @@ final class ImplementsAndHasNoConfiguredCodeSampleRector implements Configurable
// TODO: Implement configure() method.
}
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('Turns static call to function call.', [
return new RuleDefinition('Turns static call to function call.', [
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample(
'OldClass::oldMethod("args");',
'new_function("args");'

View File

@ -2,8 +2,8 @@
namespace Rector\PHPStanExtensions\Tests\Rule\ConfigurableRectorRule\Fixture;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Rector\PHPStanExtensions\Tests\Rule\ConfigurableRectorRule\Source\AbstractClassImplementsConfigurableInterface;
use Rector\Transform\ValueObject\StaticCallToFuncCall;
@ -19,10 +19,10 @@ class ImplementsThroughAbstractClassRector extends AbstractClassImplementsConfig
// TODO: Implement configure() method.
}
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('Turns static call to function call.', [
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample(
return new RuleDefinition('Turns static call to function call.', [
new ConfiguredCodeSample(
'OldClass::oldMethod("args");',
'new_function("args");',
[

View File

@ -5,8 +5,8 @@ declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\ConfigurableRectorRule\Fixture;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Rector\Transform\ValueObject\StaticCallToFuncCall;
final class NotImplementsAndHasConfiguredCodeSampleRector
@ -16,10 +16,10 @@ final class NotImplementsAndHasConfiguredCodeSampleRector
*/
public const STATIC_CALLS_TO_FUNCTIONS = 'static_calls_to_functions';
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('Turns static call to function call.', [
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample(
return new RuleDefinition('Turns static call to function call.', [
new ConfiguredCodeSample(
'OldClass::oldMethod("args");',
'new_function("args");',
[

View File

@ -6,15 +6,15 @@ namespace Rector\PHPStanExtensions\Tests\Rule\ConfigurableRectorRule\Fixture;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Rector\Transform\ValueObject\StaticCallToFuncCall;
final class NotImplementsAndHasNoConfiguredCodeSampleRector
{
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('Turns static call to function call.', [
return new RuleDefinition('Turns static call to function call.', [
new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample(
'OldClass::oldMethod("args");',
'new_function("args");'

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\RectorRuleAndValueObjectHaveSameStartsRule\Fixture;
use Rector\Generic\Rector\ClassMethod\ChangeMethodVisibilityRector;
use Rector\PHPStanExtensions\Tests\Rule\RectorRuleAndValueObjectHaveSameStartsRule\Source\ConfigureValueObject;
use function Rector\SymfonyPhpConfig\inline_value_objects;
return static function (object $random): void {
$random->set(ChangeMethodVisibilityRector::class)
->call('configure', [[
ChangeMethodVisibilityRector::METHOD_VISIBILITIES => inline_value_objects([
new ConfigureValueObject(),
new ConfigureValueObject(),
]),
]]);
};

View File

@ -23,13 +23,14 @@ final class RectorRuleAndValueObjectHaveSameStartsRuleTest extends AbstractServi
public function provideData(): Iterator
{
yield [__DIR__ . '/Fixture/HaveSameStarts.php', []];
yield [__DIR__ . '/Fixture/SkipDifferentType.php', []];
yield [__DIR__ . '/Fixture/SkipNoCall.php', []];
yield [__DIR__ . '/Fixture/SkipNoCallConfigure.php', []];
yield [__DIR__ . '/Fixture/SkipNoInlineValueObjects.php', []];
yield [__DIR__ . '/Fixture/SkipConfigureValueObjectImplementsInterface.php', []];
$errorMessage = sprintf(
RectorRuleAndValueObjectHaveSameStartsRule::ERROR,
RectorRuleAndValueObjectHaveSameStartsRule::ERROR_MESSAGE,
'ConfigureValueObject',
'ChangeMethodVisibility'
);
@ -40,7 +41,7 @@ final class RectorRuleAndValueObjectHaveSameStartsRuleTest extends AbstractServi
{
return $this->getRuleFromConfig(
RectorRuleAndValueObjectHaveSameStartsRule::class,
__DIR__ . '/../../../config/phpstan-extensions.neon'
__DIR__ . '/config/configured_rule.neon'
);
}
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Rector\PHPStanExtensions\Tests\Rule\RectorRuleAndValueObjectHaveSameStartsRule\Source;
class ConfigureValueObjectWithInterface implements ValueObjectInterface
{

View File

@ -0,0 +1,10 @@
services:
-
class: Rector\PHPStanExtensions\Rule\RectorRuleAndValueObjectHaveSameStartsRule
tags: [phpstan.rules.rule]
- Symplify\PHPStanRules\Naming\SimpleNameResolver
- PhpParser\NodeFinder
- PhpParser\ConstExprEvaluator
- Rector\PHPStanExtensions\NodeAnalyzer\TypeAndNameAnalyzer
- Rector\PHPStanExtensions\NodeAnalyzer\SymfonyConfigRectorValueObjectResolver

View File

@ -7,7 +7,7 @@ namespace Rector\PHPStanExtensions\Tests\Rule\RequireRectorCategoryByGetNodeType
use PhpParser\Node;
use PhpParser\Node\Scalar\String_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class ChangeSomethingRector extends AbstractRector
{
@ -20,7 +20,7 @@ final class ChangeSomethingRector extends AbstractRector
{
}
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
}
}

View File

@ -8,7 +8,7 @@ use PhpParser\Node;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\RectorDefinition;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class SkipSubtypeRector extends AbstractRector
{
@ -21,7 +21,7 @@ final class SkipSubtypeRector extends AbstractRector
{
}
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
public function getRuleDefinition(): RuleDefinition
{
}
}

View File

@ -67,8 +67,8 @@ final class ValidateFixtureSuffixCommand extends Command
*/
private function getInvalidFixtureFileInfos(): array
{
$finder = (new Finder())
->files()
$finder = new Finder();
$finder = $finder->files()
->name('#\.inc$#')
->name('#\.php#')
->notName('#\.php\.inc$#')