Clean PHPStan errors (#2475)

This commit is contained in:
Tomas Votruba 2022-06-10 16:43:16 +02:00 committed by GitHub
parent a6fb82623b
commit 797cb38b88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 204 additions and 99 deletions

View File

@ -32,9 +32,9 @@ abstract class AbstractNodeTypeResolverTest extends AbstractTestCase
* @param class-string<T> $type
* @return T[]
*/
protected function getNodesForFileOfType(string $file, string $type): array
protected function getNodesForFileOfType(string $filepath, string $type): array
{
$nodes = $this->testingParser->parseFileToDecoratedNodes($file);
$nodes = $this->testingParser->parseFileToDecoratedNodes($filepath);
return $this->betterNodeFinder->findInstanceOf($nodes, $type);
}
}

View File

@ -14,6 +14,7 @@ use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Exception\ShouldNotHappenException;
@ -82,12 +83,10 @@ final class ClassRenamePhpDocNodeVisitor extends AbstractPhpDocNodeVisitor
}
// make sure to compare FQNs
if ($staticType instanceof ShortenedObjectType) {
$staticType = new ObjectType($staticType->getFullyQualifiedName());
}
$objectType = $this->expandShortenedObjectType($staticType);
foreach ($this->oldToNewTypes as $oldToNewType) {
if (! $staticType->equals($oldToNewType->getOldType())) {
if (! $objectType->equals($oldToNewType->getOldType())) {
continue;
}
@ -169,4 +168,13 @@ final class ClassRenamePhpDocNodeVisitor extends AbstractPhpDocNodeVisitor
return $name;
}
private function expandShortenedObjectType(Type $type): ObjectType|Type
{
if ($type instanceof ShortenedObjectType) {
return new ObjectType($type->getFullyQualifiedName());
}
return $type;
}
}

View File

@ -41,12 +41,7 @@ final class ClassStringTypeMapper implements TypeMapperInterface
$attributeAwareIdentifierTypeNode = new IdentifierTypeNode('class-string');
if ($type instanceof GenericClassStringType) {
$genericType = $type->getGenericType();
if ($genericType instanceof ObjectType) {
$className = $genericType->getClassName();
$className = $this->normalizeType($className);
$genericType = new ObjectType($className);
}
$genericType = $this->resolveGenericObjectType($type);
$genericTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($genericType, $typeKind);
return new GenericTypeNode($attributeAwareIdentifierTypeNode, [$genericTypeNode]);
@ -81,4 +76,17 @@ final class ClassStringTypeMapper implements TypeMapperInterface
return $classType;
}
private function resolveGenericObjectType(GenericClassStringType $genericClassStringType): ObjectType|Type
{
$genericType = $genericClassStringType->getGenericType();
if (! $genericType instanceof ObjectType) {
return $genericType;
}
$className = $genericType->getClassName();
$className = $this->normalizeType($className);
return new ObjectType($className);
}
}

View File

@ -26,26 +26,33 @@ final class PhpParserNodeMapper
public function mapToPHPStanType(Node $node): Type
{
if ($node::class === Name::class && $node->hasAttribute(AttributeKey::NAMESPACED_NAME)) {
$node = new FullyQualified($node->getAttribute(AttributeKey::NAMESPACED_NAME));
}
$nameOrExpr = $this->expandedNamespacedName($node);
foreach ($this->phpParserNodeMappers as $phpParserNodeMapper) {
if (! is_a($node, $phpParserNodeMapper->getNodeType())) {
if (! is_a($nameOrExpr, $phpParserNodeMapper->getNodeType())) {
continue;
}
// do not let Expr collect all the types
// note: can be solve later with priorities on mapper interface, making this last
if ($phpParserNodeMapper->getNodeType() !== Expr::class) {
return $phpParserNodeMapper->mapToPHPStan($node);
return $phpParserNodeMapper->mapToPHPStan($nameOrExpr);
}
if (! $node instanceof String_) {
return $phpParserNodeMapper->mapToPHPStan($node);
if (! $nameOrExpr instanceof String_) {
return $phpParserNodeMapper->mapToPHPStan($nameOrExpr);
}
}
throw new NotImplementedYetException($node::class);
throw new NotImplementedYetException($nameOrExpr::class);
}
private function expandedNamespacedName(Node $node): Node|FullyQualified
{
if ($node::class === Name::class && $node->hasAttribute(AttributeKey::NAMESPACED_NAME)) {
return new FullyQualified($node->getAttribute(AttributeKey::NAMESPACED_NAME));
}
return $node;
}
}

View File

@ -38,13 +38,13 @@ final class TestingParser
/**
* @return Node[]
*/
public function parseFileToDecoratedNodes(string $file): array
public function parseFileToDecoratedNodes(string $filepath): array
{
// autoload file
require_once $file;
require_once $filepath;
$smartFileInfo = new SmartFileInfo($file);
$this->parameterProvider->changeParameter(Option::SOURCE, [$file]);
$smartFileInfo = new SmartFileInfo($filepath);
$this->parameterProvider->changeParameter(Option::SOURCE, [$filepath]);
$nodes = $this->rectorParser->parseFile($smartFileInfo);

View File

@ -731,4 +731,55 @@ parameters:
message: '#@\\ini_set\(.*\)" is forbidden to use#'
path: bin/rector.php
- '#New objects with "\$.*" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
# should be fixed in symplify next
-
message: '#New objects with "\$arrayItem" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: src/PhpParser/Node/NodeFactory.php
# reducing original node
-
message: '#New objects with "\$bitwiseOr" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: rules/DowngradePhp72/NodeManipulator/BitwiseFlagCleaner.php
# faking node to invoke scope callable on attribute
-
message: '#New objects with "\$node" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: src/Application/ChangedNodeScopeRefresher.php
# added nullable type
-
message: '#New objects with "\$mappedCurrentParamType" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: rules/DowngradePhp72/PhpDoc/NativeParamToPhpDocDecorator.php
-
message: '#New objects with "\$node" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: src/Rector/AbstractRector.php
# joined items
-
message: '#New objects with "\$expr" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: rules/DowngradePhp55/Rector/Isset_/DowngradeArbitraryExpressionArgsToEmptyAndIssetRector.php
# mixed type correction
-
message: '#New objects with "\$(first|second)KeyType" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: packages/NodeTypeResolver/TypeComparator/TypeComparator.php
-
message: '#New objects with "\$staticType" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: packages/NodeTypeResolver/PhpDocNodeVisitor/NameImportingPhpDocNodeVisitor.php
-
message: '#New objects with "\$replaceIntoType" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: rules/CodeQuality/NodeManipulator/ClassMethodReturnTypeManipulator.php
-
path: rules/DowngradePhp74/Rector/ClassMethod/DowngradeCovariantReturnTypeRector.php
message: '#New objects with "\$parentReturnTypeNode" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
-
path: rules/DowngradePhp80/NodeAnalyzer/UnnamedArgumentResolver.php
message: '#New objects with "\$functionLikeReflection" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
-
message: '#New objects with "\$fullyQualifiedObjectType" name are overridden\. This can lead to unwanted bugs, please pick a different name to avoid it#'
path: packages/NodeTypeResolver/PhpDocNodeVisitor/NameImportingPhpDocNodeVisitor.php

View File

@ -15,14 +15,13 @@ use PhpParser\Node\NullableType;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\NodeAnalyzer\ParamAnalyzer;
use Rector\DowngradePhp72\UnionTypeFactory;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
@ -36,7 +35,8 @@ final class ClassMethodParameterTypeManipulator
private readonly NodeTypeResolver $nodeTypeResolver,
private readonly ParamAnalyzer $paramAnalyzer,
private readonly NodeNameResolver $nodeNameResolver,
private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser
private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
private readonly UnionTypeFactory $unionTypeFactory,
) {
}
@ -69,10 +69,10 @@ final class ClassMethodParameterTypeManipulator
private function refactorParamTypeHint(Param $param, Identifier|Name|NullableType $replaceIntoType): void
{
if ($this->paramAnalyzer->isNullable($param) && ! $replaceIntoType instanceof NullableType) {
$replaceIntoType = new NullableType($replaceIntoType);
$param->type = new NullableType($replaceIntoType);
} else {
$param->type = $replaceIntoType;
}
$param->type = $replaceIntoType;
}
private function refactorParamDocBlock(Param $param, ClassMethod $classMethod, Type $phpDocType): void
@ -83,12 +83,7 @@ final class ClassMethodParameterTypeManipulator
}
if ($this->paramAnalyzer->isNullable($param)) {
if ($phpDocType instanceof UnionType) {
// Adding a UnionType into a new UnionType throws an exception so we need to "unpack" the types
$phpDocType = new UnionType([...$phpDocType->getTypes(), new NullType()]);
} else {
$phpDocType = new UnionType([$phpDocType, new NullType()]);
}
$phpDocType = $this->unionTypeFactory->createNullableUnionType($phpDocType);
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);

View File

@ -4,16 +4,16 @@ declare(strict_types=1);
namespace Rector\CodeQuality\NodeManipulator;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\DowngradePhp72\UnionTypeFactory;
use Rector\NodeTypeResolver\NodeTypeResolver;
final class ClassMethodReturnTypeManipulator
@ -21,7 +21,8 @@ final class ClassMethodReturnTypeManipulator
public function __construct(
private readonly PhpDocInfoFactory $phpDocInfoFactory,
private readonly PhpDocTypeChanger $phpDocTypeChanger,
private readonly NodeTypeResolver $nodeTypeResolver
private readonly NodeTypeResolver $nodeTypeResolver,
private readonly UnionTypeFactory $unionTypeFactory,
) {
}
@ -32,7 +33,7 @@ final class ClassMethodReturnTypeManipulator
Type $phpDocType
): ?ClassMethod {
$returnType = $classMethod->returnType;
if ($returnType === null) {
if (! $returnType instanceof Node) {
return null;
}
@ -52,12 +53,7 @@ final class ClassMethodReturnTypeManipulator
}
if ($isNullable) {
if ($phpDocType instanceof UnionType) {
// Adding a UnionType into a new UnionType throws an exception so we need to "unpack" the types
$phpDocType = new UnionType([...$phpDocType->getTypes(), new NullType()]);
} else {
$phpDocType = new UnionType([$phpDocType, new NullType()]);
}
$phpDocType = $this->unionTypeFactory->createNullableUnionType($phpDocType);
if (! $replaceIntoType instanceof NullableType) {
$replaceIntoType = new NullableType($replaceIntoType);

View File

@ -206,9 +206,9 @@ CODE_SAMPLE
private function resolveString(bool $isNegated, Expr $expr): Identical | NotIdentical | BooleanAnd | BooleanOr
{
$string = new String_('');
$emptyString = new String_('');
$identical = $this->resolveIdentical($expr, $isNegated, $string);
$identical = $this->resolveIdentical($expr, $isNegated, $emptyString);
$value = $this->valueResolver->getValue($expr);
@ -220,8 +220,8 @@ CODE_SAMPLE
$length = strlen((string) $value);
if ($length === 1) {
$string = new String_('0');
return $this->resolveIdentical($expr, $isNegated, $string);
$zeroString = new String_('0');
return $this->resolveIdentical($expr, $isNegated, $zeroString);
}
return $identical;

View File

@ -223,11 +223,11 @@ CODE_SAMPLE
$this->removeNode($return);
$this->removeNode($expression);
$return = new Return_($expr);
$exprReturn = new Return_($expr);
$this->varTagRemover->removeVarPhpTagValueNodeIfNotComment($expression, $unionType);
$this->mirrorComments($return, $expression);
$this->mirrorComments($exprReturn, $expression);
return $return;
return $exprReturn;
}
private function shouldSkip(If_ $if): bool

View File

@ -7,6 +7,7 @@ namespace Rector\CodingStyle\Rector\MethodCall;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ObjectType;
use Rector\CodingStyle\Enum\PreferenceSelfThis;
@ -161,11 +162,11 @@ CODE_SAMPLE
// avoid adding dynamic method call to static method
$classMethod = $this->betterNodeFinder->findParentByTypes($node, [ClassMethod::class]);
if (! $classMethod instanceof ClassMethod) {
return $this->nodeFactory->createMethodCall(self::THIS, $name, $node->args);
return $this->nodeFactory->createMethodCall(new Variable(self::THIS), $name, $node->args);
}
if (! $classMethod->isStatic()) {
return $this->nodeFactory->createMethodCall(self::THIS, $name, $node->args);
return $this->nodeFactory->createMethodCall(new Variable(self::THIS), $name, $node->args);
}
return null;

View File

@ -141,8 +141,9 @@ CODE_SAMPLE
private function joinWithBooleanAnd(array $exprs): Expr
{
$expr = $exprs[0];
$nbExprs = count($exprs);
for ($i = 1; $i < $nbExprs; ++$i) {
$exprCount = count($exprs);
for ($i = 1; $i < $exprCount; ++$i) {
$expr = new BooleanAnd($expr, $exprs[$i]);
}

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Rector\DowngradePhp72\PhpDoc;
use PhpParser\Node\Expr;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\NullType;
@ -38,12 +39,19 @@ final class NativeParamToPhpDocDecorator
$mappedCurrentParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type);
// add default null type
if ($param->default !== null && $this->valueResolver->isNull($param->default) && ! TypeCombinator::containsNull(
$mappedCurrentParamType
)) {
if ($this->isParamNullable($param) && ! TypeCombinator::containsNull($mappedCurrentParamType)) {
$mappedCurrentParamType = new UnionType([$mappedCurrentParamType, new NullType()]);
}
$this->phpDocTypeChanger->changeParamType($phpDocInfo, $mappedCurrentParamType, $param, $paramName);
}
private function isParamNullable(Param $param): bool
{
if (! $param->default instanceof Expr) {
return false;
}
return $this->valueResolver->isNull($param->default);
}
}

View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Rector\DowngradePhp72;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
final class UnionTypeFactory
{
public function createNullableUnionType(Type $type): UnionType
{
if ($type instanceof UnionType) {
// Adding a UnionType into a new UnionType throws an exception so we need to "unpack" the types
return new UnionType([...$type->getTypes(), new NullType()]);
}
return new UnionType([$type, new NullType()]);
}
}

View File

@ -73,10 +73,11 @@ CODE_SAMPLE
}
$getName = new MethodCall($node->var, 'getName');
$node = new MethodCall($node->var, 'getDeclaringClass');
$node = new MethodCall($node, 'getDefaultProperties');
$node = new ArrayDimFetch($node, $getName);
$getDeclaringClassMethodCall = new MethodCall($node->var, 'getDeclaringClass');
$getDefaultPropertiesMethodCall = new MethodCall($getDeclaringClassMethodCall, 'getDefaultProperties');
return new Coalesce($node, $this->nodeFactory->createNull());
$arrayDimFetch = new ArrayDimFetch($getDefaultPropertiesMethodCall, $getName);
return new Coalesce($arrayDimFetch, $this->nodeFactory->createNull());
}
}

View File

@ -138,14 +138,10 @@ CODE_SAMPLE
// special case
if ($invertedCondition instanceof BooleanNot && $invertedCondition->expr instanceof BooleanAnd) {
$leftExpr = $this->negateOrDeNegate($invertedCondition->expr->left);
$if = new If_($leftExpr);
$if->stmts[] = new Continue_();
$foreach->stmts[] = $if;
$foreach->stmts[] = $this->createIfContinue($leftExpr);
$rightExpr = $this->negateOrDeNegate($invertedCondition->expr->right);
$if = new If_($rightExpr);
$if->stmts[] = new Continue_();
$foreach->stmts[] = $if;
$foreach->stmts[] = $this->createIfContinue($rightExpr);
return;
}
@ -201,4 +197,11 @@ CODE_SAMPLE
return new BooleanNot($expr);
}
private function createIfContinue(Expr $expr): If_
{
return new If_($expr, [
'stmts' => [new Continue_()],
]);
}
}

View File

@ -235,34 +235,16 @@ final class NodeFactory
/**
* @param mixed[] $arguments
*/
public function createMethodCall(string | Expr $variable, string $method, array $arguments = []): MethodCall
public function createMethodCall(Expr|string $exprOrVariableName, string $method, array $arguments = []): MethodCall
{
if (is_string($variable)) {
$variable = new Variable($variable);
}
if ($variable instanceof PropertyFetch) {
$variable = new PropertyFetch($variable->var, $variable->name);
}
if ($variable instanceof StaticPropertyFetch) {
$variable = new StaticPropertyFetch($variable->class, $variable->name);
}
if ($variable instanceof MethodCall) {
$variable = new MethodCall($variable->var, $variable->name, $variable->args);
}
return $this->builderFactory->methodCall($variable, $method, $arguments);
$callerExpr = $this->createMethodCaller($exprOrVariableName);
return $this->builderFactory->methodCall($callerExpr, $method, $arguments);
}
public function createPropertyFetch(string | Expr $variable, string $property): PropertyFetch
public function createPropertyFetch(string | Expr $variableNameOrExpr, string $property): PropertyFetch
{
if (is_string($variable)) {
$variable = new Variable($variable);
}
return $this->builderFactory->propertyFetch($variable, $property);
$fetcherExpr = is_string($variableNameOrExpr) ? new Variable($variableNameOrExpr) : $variableNameOrExpr;
return $this->builderFactory->propertyFetch($fetcherExpr, $property);
}
/**
@ -577,7 +559,7 @@ final class NodeFactory
$arrayItem = new ArrayItem($item->value);
}
if ($arrayItem !== null) {
if ($arrayItem instanceof ArrayItem) {
$this->decorateArrayItemWithKey($key, $arrayItem);
return $arrayItem;
}
@ -638,7 +620,7 @@ final class NodeFactory
/**
* @param string|ObjectReference::* $className
*/
private function createName(string $className): FullyQualified|Name
private function createName(string $className): Name|FullyQualified
{
if (in_array($className, [ObjectReference::PARENT, ObjectReference::SELF, ObjectReference::STATIC], true)) {
return new Name($className);
@ -646,4 +628,26 @@ final class NodeFactory
return new FullyQualified($className);
}
private function createMethodCaller(
Expr|string $exprOrVariableName
): PropertyFetch|Variable|MethodCall|StaticPropertyFetch|Expr {
if (is_string($exprOrVariableName)) {
return new Variable($exprOrVariableName);
}
if ($exprOrVariableName instanceof PropertyFetch) {
return new PropertyFetch($exprOrVariableName->var, $exprOrVariableName->name);
}
if ($exprOrVariableName instanceof StaticPropertyFetch) {
return new StaticPropertyFetch($exprOrVariableName->class, $exprOrVariableName->name);
}
if ($exprOrVariableName instanceof MethodCall) {
return new MethodCall($exprOrVariableName->var, $exprOrVariableName->name, $exprOrVariableName->args);
}
return $exprOrVariableName;
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Core\Tests\Issues\AliasedImportDouble\Fixture;
namespace Rector\Core\Tests\Issues\AutoImportBackslashed\Fixture;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
@ -20,7 +20,7 @@ final class TernaryReturn
-----
<?php
namespace Rector\Core\Tests\Issues\AliasedImportDouble\Fixture;
namespace Rector\Core\Tests\Issues\AutoImportBackslashed\Fixture;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;