[CodingStyle] Add NullableCompareToNullRector + add NodeFactoryTrait + cleanup

This commit is contained in:
Tomas Votruba 2018-11-03 10:35:05 +01:00
parent 01b5ebfdad
commit c3558f65c4
34 changed files with 372 additions and 135 deletions

View File

@ -37,6 +37,7 @@
"Rector\\": "src",
"Rector\\ContributorTools\\": "packages/ContributorTools/src",
"Rector\\ConsoleDiffer\\": "packages/ConsoleDiffer/src",
"Rector\\CodingStyle\\": "packages/CodingStyle/src",
"Rector\\Guzzle\\": "packages/Guzzle/src",
"Rector\\CodeQuality\\": "packages/CodeQuality/src",
"Rector\\DomainDrivenDesign\\": "packages/DomainDrivenDesign/src",
@ -63,6 +64,7 @@
"Rector\\NodeTypeResolver\\Tests\\": "packages/NodeTypeResolver/tests",
"Rector\\CakePHP\\Tests\\": "packages/CakePHP/tests",
"Rector\\CodeQuality\\Tests\\": "packages/CodeQuality/tests",
"Rector\\CodingStyle\\Tests\\": "packages/CodingStyle/tests",
"Rector\\DomainDrivenDesign\\Tests\\": "packages/DomainDrivenDesign/tests",
"Rector\\Guzzle\\Tests\\": "packages/Guzzle/tests",
"Rector\\Php\\Tests\\": "packages/Php/tests",

View File

@ -0,0 +1,2 @@
services:
Rector\CodingStyle\Rector\If_\NullableCompareToNullRector: ~

View File

@ -27,6 +27,7 @@ services:
Symplify\CodingStandard\Sniffs\DependencyInjection\NoClassInstantiationSniff:
extraAllowedClasses:
- 'PHPStan\Type\*'
- 'PhpParser\Node\*'
- 'Symplify\PackageBuilder\*'
- 'PhpParser\Comment\Doc'

View File

@ -8,7 +8,6 @@ use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Return_;
@ -31,11 +30,6 @@ final class ForeachToInArrayRector extends AbstractRector
*/
private $constFetchAnalyzer;
/**
* @var Return_
*/
private $returnNodeToRemove;
public function __construct(NodeFactory $nodeFactory, ConstFetchAnalyzer $constFetchAnalyzer)
{
$this->nodeFactory = $nodeFactory;
@ -108,8 +102,8 @@ CODE_SAMPLE
$inArrayFunctionCall = $this->createInArrayFunction($condition, $ifCondition, $foreachNode);
$this->returnNodeToRemove = $foreachNode->getAttribute(Attribute::NEXT_NODE);
$this->removeNode($this->returnNodeToRemove);
$returnNodeToRemove = $foreachNode->getAttribute(Attribute::NEXT_NODE);
$this->removeNode($returnNodeToRemove);
/** @var Return_ $returnNode */
$returnNode = $firstNodeInsideForeach->stmts[0];
@ -176,9 +170,9 @@ CODE_SAMPLE
$arguments = $this->nodeFactory->createArgs([$condition, $foreachNode->expr]);
if ($ifCondition instanceof Identical) {
$arguments[] = $this->nodeFactory->createArg($this->nodeFactory->createTrueConstant());
$arguments[] = $this->nodeFactory->createArg($this->createTrue());
}
return new FuncCall(new Name('in_array'), $arguments);
return $this->createFunction('in_array', $arguments);
}
}

View File

@ -4,7 +4,6 @@ namespace Rector\CodeQuality\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@ -47,6 +46,6 @@ final class SimplifyFuncGetArgsCountRector extends AbstractRector
return $node;
}
return new FuncCall(new Name('func_num_args'));
return $this->createFunction('func_num_args');
}
}

View File

@ -9,7 +9,6 @@ use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@ -49,7 +48,7 @@ final class SimplifyArraySearchRector extends AbstractRector
[$arraySearchFuncCallNode, $boolConstFetchNode] = $match;
$inArrayFuncCall = new FuncCall(new Name('in_array'), [
$inArrayFuncCall = $this->createFunction('in_array', [
$arraySearchFuncCallNode->args[0],
$arraySearchFuncCallNode->args[1],
]);

View File

@ -0,0 +1,71 @@
<?php declare(strict_types=1);
namespace Rector\CodingStyle\Rector\If_;
use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Stmt\If_;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
final class NullableCompareToNullRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Changes negate of empty comparison of nullable value to explicit === or !== compare',
[
new CodeSample(
<<<'CODE_SAMPLE'
/** @var stdClass|null $value */
if ($value) {
}
if (!$value) {
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
/** @var stdClass|null $value */
if ($value !== null) {
}
if ($value === null) {
}
CODE_SAMPLE
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [If_::class];
}
/**
* @param If_ $node
*/
public function refactor(Node $node): ?Node
{
if ($this->isNullableType($node->cond)) {
$node->cond = new NotIdentical($node->cond, $this->createNull());
return $node;
}
if ($node->cond instanceof BooleanNot && $this->isNullableType($node->cond->expr)) {
$node->cond = new Identical($node->cond->expr, $this->createNull());
return $node;
}
return null;
}
}

View File

@ -0,0 +1,9 @@
<?php
/** @var stdClass|null $value */
$value = null;
if ($value !== null) {
}
if ($value === null) {
}

View File

@ -0,0 +1,6 @@
<?php
/** @var stdClass|null|string $value */
$value = null;
if ($value !== null) {
}

View File

@ -0,0 +1,22 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\If_\NullableCompareToNullRector\Wrong;
use stdClass;
class SomeClass
{
public function run()
{
if ($this->getNullOrObject() !== null) {
return true;
}
return false;
}
private function getNullOrObject(): ?stdClass
{
return null;
}
}

View File

@ -0,0 +1,32 @@
<?php declare(strict_types=1);
namespace Rector\CodingStyle\Tests\Rector\If_\NullableCompareToNullRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
/**
* @covers \Rector\CodingStyle\Rector\If_\NullableCompareToNullRector
*/
final class NullableCompareToNullRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideWrongToFixedFiles()
*/
public function test(string $wrong, string $fixed): void
{
$this->doTestFileMatchesExpectedContent($wrong, $fixed);
}
public function provideWrongToFixedFiles(): Iterator
{
yield [__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'];
yield [__DIR__ . '/Wrong/wrong2.php.inc', __DIR__ . '/Correct/correct2.php.inc'];
yield [__DIR__ . '/Wrong/wrong3.php.inc', __DIR__ . '/Correct/correct3.php.inc'];
}
protected function provideConfig(): string
{
return __DIR__ . '/config.yml';
}
}

View File

@ -0,0 +1,9 @@
<?php
/** @var stdClass|null $value */
$value = null;
if ($value) {
}
if (!$value) {
}

View File

@ -0,0 +1,6 @@
<?php
/** @var stdClass|null|string $value */
$value = null;
if ($value) {
}

View File

@ -0,0 +1,22 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\If_\NullableCompareToNullRector\Wrong;
use stdClass;
class SomeClass
{
public function run()
{
if ($this->getNullOrObject()) {
return true;
}
return false;
}
private function getNullOrObject(): ?stdClass
{
return null;
}
}

View File

@ -0,0 +1,2 @@
services:
Rector\CodingStyle\Rector\If_\NullableCompareToNullRector: ~

View File

@ -0,0 +1,95 @@
<?php declare(strict_types=1);
namespace Rector\NodeTypeResolver;
use Countable;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Accessory\HasOffsetType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use PHPStan\Type\UnionType;
use Rector\NodeTypeResolver\Node\Attribute;
final class NodeTypeAnalyzer
{
public function isStringType(Node $node): bool
{
if (! $node instanceof Expr) {
return false;
}
/** @var Scope $nodeScope */
$nodeScope = $node->getAttribute(Attribute::SCOPE);
$nodeType = $nodeScope->getType($node);
return $nodeType instanceof StringType;
}
/**
* e.g. string|null, ObjectNull|null
*/
public function isNullableType(Node $node): bool
{
if (! $node instanceof Expr) {
return false;
}
/** @var Scope $nodeScope */
$nodeScope = $node->getAttribute(Attribute::SCOPE);
$nodeType = $nodeScope->getType($node);
if (! $nodeType instanceof UnionType) {
return false;
}
return $nodeType->accepts(new NullType(), true)->yes();
}
public function isBoolType(Node $node): bool
{
if (! $node instanceof Expr) {
return false;
}
/** @var Scope $nodeScope */
$nodeScope = $node->getAttribute(Attribute::SCOPE);
$nodeType = $nodeScope->getType($node);
return $nodeType instanceof BooleanType;
}
public function isCountableType(Node $node): bool
{
if (! $node instanceof Expr) {
return false;
}
/** @var Scope $nodeScope */
$nodeScope = $node->getAttribute(Attribute::SCOPE);
$nodeType = $nodeScope->getType($node);
if ($nodeType instanceof ObjectType) {
return is_a($nodeType->getClassName(), Countable::class, true);
}
if ($nodeType instanceof IntersectionType) {
foreach ($nodeType->getTypes() as $intersectionNodeType) {
if ($intersectionNodeType instanceof ArrayType || $intersectionNodeType instanceof HasOffsetType) {
continue;
}
return false;
}
return true;
}
return $nodeType instanceof ArrayType;
}
}

View File

@ -3,11 +3,9 @@
namespace Rector\Php\Rector\Each;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Do_;
use PhpParser\Node\Stmt\Expression;
use Rector\NodeTypeResolver\Node\Attribute;
@ -64,16 +62,16 @@ CODE_SAMPLE
// only key: list($key, ) = each($values);
if ($listNode->items[0] && $listNode->items[1] === null) {
$keyFuncCall = $this->createFuncCallWithNameAndArgs('key', $eachFuncCall->args);
$keyFuncCall = $this->createFunction('key', $eachFuncCall->args);
return new Assign($listNode->items[0]->value, $keyFuncCall);
}
// only value: list(, $value) = each($values);
if ($listNode->items[1] && $listNode->items[0] === null) {
$nextFuncCall = $this->createFuncCallWithNameAndArgs('next', $eachFuncCall->args);
$nextFuncCall = $this->createFunction('next', $eachFuncCall->args);
$this->addNodeAfterNode($nextFuncCall, $node);
$currentFuncCall = $this->createFuncCallWithNameAndArgs('current', $eachFuncCall->args);
$currentFuncCall = $this->createFunction('current', $eachFuncCall->args);
return new Assign($listNode->items[1]->value, $currentFuncCall);
}
@ -82,16 +80,16 @@ CODE_SAMPLE
// $key = key($values);
// $value = current($values);
// next($values); - only inside a loop
$currentFuncCall = $this->createFuncCallWithNameAndArgs('current', $eachFuncCall->args);
$currentFuncCall = $this->createFunction('current', $eachFuncCall->args);
$assignCurrentNode = new Assign($listNode->items[1]->value, $currentFuncCall);
$this->addNodeAfterNode($assignCurrentNode, $node);
if ($this->isInsideDoWhile($node)) {
$nextFuncCall = $this->createFuncCallWithNameAndArgs('next', $eachFuncCall->args);
$nextFuncCall = $this->createFunction('next', $eachFuncCall->args);
$this->addNodeAfterNode($nextFuncCall, $node);
}
$keyFuncCall = $this->createFuncCallWithNameAndArgs('key', $eachFuncCall->args);
$keyFuncCall = $this->createFunction('key', $eachFuncCall->args);
return new Assign($listNode->items[0]->value, $keyFuncCall);
}
@ -117,14 +115,6 @@ CODE_SAMPLE
return $listNode->items[0] === null && $listNode->items[1] === null;
}
/**
* @param Arg[] $args
*/
private function createFuncCallWithNameAndArgs(string $name, array $args): FuncCall
{
return new FuncCall(new Name($name), $args);
}
/**
* Is inside the "do {} while ();" loop need to add "next()"
*/

View File

@ -7,7 +7,6 @@ use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\While_;
use Rector\Rector\AbstractRector;
@ -85,7 +84,7 @@ CODE_SAMPLE
$listNode = $assignNode->var;
if (count($listNode->items) === 1) { // just one argument - the key
$foreachedExpr = new FuncCall(new Name('array_keys'), [$eachFuncCall->args[0]]);
$foreachedExpr = $this->createFunction('array_keys', [$eachFuncCall->args[0]]);
} else {
$foreachedExpr = $eachFuncCall->args[0]->value;
}

View File

@ -8,7 +8,6 @@ use PhpParser\Node\Expr\BinaryOp\BooleanOr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\LNumber;
use Rector\NodeTypeResolver\Node\Attribute;
@ -73,7 +72,7 @@ CODE_SAMPLE
}
$conditionNode = new BooleanOr(
new FuncCall(new Name('is_array'), [new Arg($countedNode)]),
$this->createFunction('is_array', [new Arg($countedNode)]),
new Instanceof_($countedNode, new FullyQualified('Countable'))
);

View File

@ -8,7 +8,6 @@ use PhpParser\Node\Arg;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Expr\Variable;
@ -109,7 +108,7 @@ final class EregToPregMatchRector extends AbstractRector
private function processVariablePattern(FuncCall $funcCallNode, Variable $patternNode, string $functionName): void
{
$pregQuotePatternNode = new FuncCall(new Name('preg_quote'), [
$pregQuotePatternNode = $this->createFunction('preg_quote', [
new Arg($patternNode),
new Arg(new String_('#')),
]);
@ -155,9 +154,9 @@ final class EregToPregMatchRector extends AbstractRector
private function createTernaryWithStrlenOfFirstMatch(FuncCall $funcCallNode): Ternary
{
$arrayDimFetch = new ArrayDimFetch($funcCallNode->args[2]->value, new LNumber(0));
$strlenFuncCall = new FuncCall(new Name('strlen'), [new Arg($arrayDimFetch)]);
$strlenFuncCall = $this->createFunction('strlen', [$arrayDimFetch]);
return new Ternary($funcCallNode, $strlenFuncCall, new ConstFetch(new Name('false')));
return new Ternary($funcCallNode, $strlenFuncCall, $this->createFalse());
}
private function isCaseInsensitiveFunction(string $functionName): bool

View File

@ -72,7 +72,7 @@ CODE_SAMPLE
$node->args[$i] = new Arg($this->createConstFetch('JSON_THROW_ON_ERROR'));
} else {
// fill in blanks
$node->args[$i] = new Arg($this->createConstFetch('null'));
$node->args[$i] = new Arg($this->createNull());
}
}

View File

@ -51,7 +51,7 @@ final class RandomFunctionRector extends AbstractRector
// special case: random_int(); → random_int(0, getrandmax());
if ($newFunctionName === 'random_int' && count($node->args) === 0) {
$node->args[0] = new Arg(new LNumber(0));
$node->args[1] = new Arg(new FuncCall(new Name('mt_getrandmax')));
$node->args[1] = new Arg($this->createFunction('mt_getrandmax'));
}
return $node;

View File

@ -3,11 +3,8 @@
namespace Rector\Php\Rector\List_;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Name;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@ -48,7 +45,7 @@ final class ListSplitStringRector extends AbstractRector
return null;
}
$node->expr = new FuncCall(new Name('str_split'), [new Arg($node->expr)]);
$node->expr = $this->createFunction('str_split', [$node->expr]);
return $node;
}

View File

@ -3,13 +3,10 @@
namespace Rector\Php\Rector\List_;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Name;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@ -61,7 +58,7 @@ final class ListSwapArrayOrderRector extends AbstractRector
}
// wrap with array_reverse, to reflect reverse assign order in left
$node->expr = new FuncCall(new Name('array_reverse'), [new Arg($node->expr)]);
$node->expr = $this->createFunction('array_reverse', [$node->expr]);
return $node;
}

View File

@ -4,8 +4,6 @@ namespace Rector\Php\Rector\Unset_;
use PhpParser\Node;
use PhpParser\Node\Expr\Cast\Unset_;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Name;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@ -40,6 +38,6 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
return new ConstFetch(new Name('null'));
return $this->createNull();
}
}

View File

@ -10,7 +10,6 @@ use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\Variable;
@ -186,7 +185,7 @@ CODE_SAMPLE
// set default data in between
if ($position + 1 !== $optionsPosition) {
if (! isset($methodCallNode->args[$position + 1])) {
$methodCallNode->args[$position + 1] = new Arg(new ConstFetch(new Name('null')));
$methodCallNode->args[$position + 1] = new Arg($this->createNull());
}
}
@ -291,11 +290,12 @@ CODE_SAMPLE
$optionsDefaults = new Array_();
foreach (array_keys($namesToArgs) as $optionName) {
$optionsDefaults->items[] = new ArrayItem(new ConstFetch(new Name('null')), new String_($optionName));
$optionsDefaults->items[] = new ArrayItem($this->createNull(), new String_($optionName));
}
$setDefaultsMethodCall = new MethodCall($resolverParamNode->var, new Identifier('setDefaults'));
$setDefaultsMethodCall->args[] = new Arg($optionsDefaults);
$setDefaultsMethodCall = new MethodCall($resolverParamNode->var, new Identifier('setDefaults'), [
new Arg($optionsDefaults),
]);
$configureOptionsClassMethodNode = $this->builderFactory->method('configureOptions')
->makePublic()

View File

@ -75,7 +75,7 @@ final class VarDumperTestTraitMethodArgsRector extends AbstractRector
if ($node->args[2]->value instanceof String_) {
$node->args[3] = $node->args[2];
$node->args[2] = $this->nodeFactory->createArg($this->nodeFactory->createNullConstant());
$node->args[2] = $this->nodeFactory->createArg($this->createNull());
return $node;
}

View File

@ -5,9 +5,7 @@ namespace Rector\Symfony\Rector\Yaml;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Constant\ConstantStringType;
use Rector\NodeTypeResolver\Node\Attribute;
@ -51,7 +49,7 @@ final class ParseFileRector extends AbstractRector
return null;
}
$fileGetContentsFunCallNode = new FuncCall(new Name('file_get_contents'), [$node->args[0]]);
$fileGetContentsFunCallNode = $this->createFunction('file_get_contents', [$node->args[0]]);
$node->args[0] = new Arg($fileGetContentsFunCallNode);
return $node;

View File

@ -5,4 +5,4 @@ namespace Rector\Symfony\Tests\Rector\New_\StringToArrayArgumentProcessRector\So
final class Process
{
}
}

View File

@ -5,4 +5,4 @@ namespace Rector\Symfony\Tests\Rector\New_\StringToArrayArgumentProcessRector\So
final class ProcessHelper
{
}
}

View File

@ -11,7 +11,6 @@ use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
@ -55,22 +54,6 @@ final class NodeFactory
$this->typeAnalyzer = $typeAnalyzer;
}
/**
* Creates "null"
*/
public function createNullConstant(): ConstFetch
{
return BuilderHelpers::normalizeValue(null);
}
/**
* Creates "true"
*/
public function createTrueConstant(): ConstFetch
{
return BuilderHelpers::normalizeValue(true);
}
/**
* Creates "\SomeClass::CONSTANT"
*/
@ -168,9 +151,7 @@ final class NodeFactory
*/
public function createArg($argument): Arg
{
$value = BuilderHelpers::normalizeValue($argument);
return new Arg($value);
return new Arg(BuilderHelpers::normalizeValue($argument));
}
/**
@ -179,8 +160,7 @@ final class NodeFactory
*/
public function createNamespace(string $namespace): Namespace_
{
return $this->builderFactory->namespace($namespace)
->getNode();
return new Namespace_(new Name($namespace));
}
public function createParam(string $name, string $type): Param

View File

@ -17,6 +17,7 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
use BetterStandardPrinterTrait;
use ClassPropertyCollectorTrait;
use RemovingTrait;
use NodeFactoryTrait;
/**
* @var ExpressionAdder

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace Rector\Rector;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
/**
* This could be part of @see AbstractRector, but decopuling to trait
* makes clear what code has 1 purpose.
*/
trait NodeFactoryTrait
{
protected function createNull(): ConstFetch
{
return new ConstFetch(new Name('null'));
}
protected function createFalse(): ConstFetch
{
return new ConstFetch(new Name('false'));
}
protected function createTrue(): ConstFetch
{
return new ConstFetch(new Name('true'));
}
/**
* @param mixed[] $arguments
*/
protected function createFunction(string $name, array $arguments = []): FuncCall
{
return new FuncCall(new Name($name), $arguments);
}
}

View File

@ -2,9 +2,7 @@
namespace Rector\Rector;
use Countable;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\ClassConstFetch;
@ -13,14 +11,8 @@ use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Accessory\HasOffsetType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use Rector\NodeTypeResolver\Node\Attribute;
use Rector\NodeTypeResolver\NodeTypeAnalyzer;
use Rector\NodeTypeResolver\NodeTypeResolver;
/**
@ -34,6 +26,11 @@ trait TypeAnalyzerTrait
*/
private $nodeTypeResolver;
/**
* @var NodeTypeAnalyzer
*/
private $nodeTypeAnalyzer;
/**
* @required
*/
@ -42,6 +39,14 @@ trait TypeAnalyzerTrait
$this->nodeTypeResolver = $nodeTypeResolver;
}
/**
* @required
*/
public function setNodeTypeAnalyzer(NodeTypeAnalyzer $nodeTypeAnalyzer): void
{
$this->nodeTypeAnalyzer = $nodeTypeAnalyzer;
}
public function isType(Node $node, string $type): bool
{
$nodeTypes = $this->getTypes($node);
@ -68,57 +73,22 @@ trait TypeAnalyzerTrait
public function isStringType(Node $node): bool
{
if (! $node instanceof Expr) {
return false;
}
return $this->nodeTypeAnalyzer->isStringType($node);
}
/** @var Scope $nodeScope */
$nodeScope = $node->getAttribute(Attribute::SCOPE);
$nodeType = $nodeScope->getType($node);
return $nodeType instanceof StringType;
public function isNullableType(Node $node): bool
{
return $this->nodeTypeAnalyzer->isNullableType($node);
}
public function isBoolType(Node $node): bool
{
if (! $node instanceof Expr) {
return false;
}
/** @var Scope $nodeScope */
$nodeScope = $node->getAttribute(Attribute::SCOPE);
$nodeType = $nodeScope->getType($node);
return $nodeType instanceof BooleanType;
return $this->nodeTypeAnalyzer->isBoolType($node);
}
public function isCountableType(Node $node): bool
{
if (! $node instanceof Expr) {
return false;
}
/** @var Scope $nodeScope */
$nodeScope = $node->getAttribute(Attribute::SCOPE);
$nodeType = $nodeScope->getType($node);
if ($nodeType instanceof ObjectType) {
return is_a($nodeType->getClassName(), Countable::class, true);
}
if ($nodeType instanceof IntersectionType) {
foreach ($nodeType->getTypes() as $intersectionNodeType) {
if ($intersectionNodeType instanceof ArrayType || $intersectionNodeType instanceof HasOffsetType) {
continue;
}
return false;
}
return true;
}
return $nodeType instanceof ArrayType;
return $this->nodeTypeAnalyzer->isCountableType($node);
}
/**
@ -126,6 +96,7 @@ trait TypeAnalyzerTrait
*/
public function getTypes(Node $node): array
{
// @todo should be resolved by NodeTypeResolver internally
if ($node instanceof ClassMethod || $node instanceof ClassConst) {
return $this->nodeTypeResolver->resolve($node->getAttribute(Attribute::CLASS_NODE));
}