mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 21:38:22 +01:00
Add CompleteVarDocTypePropertyRector
This commit is contained in:
parent
b956ccd87b
commit
48cb147db4
@ -0,0 +1,54 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Node;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Scalar\DNumber;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use Rector\NodeTypeResolver\NodeTypeAnalyzer;
|
||||
use Rector\PhpParser\Node\Maintainer\ConstFetchMaintainer;
|
||||
|
||||
final class NodeToStringTypeResolver
|
||||
{
|
||||
/**
|
||||
* @var NodeTypeAnalyzer
|
||||
*/
|
||||
private $nodeTypeAnalyzer;
|
||||
|
||||
/**
|
||||
* @var ConstFetchMaintainer
|
||||
*/
|
||||
private $constFetchMaintainer;
|
||||
|
||||
public function __construct(NodeTypeAnalyzer $nodeTypeAnalyzer, ConstFetchMaintainer $constFetchMaintainer)
|
||||
{
|
||||
$this->nodeTypeAnalyzer = $nodeTypeAnalyzer;
|
||||
$this->constFetchMaintainer = $constFetchMaintainer;
|
||||
}
|
||||
|
||||
public function resolver(Node $node): string
|
||||
{
|
||||
if ($node instanceof LNumber) {
|
||||
return 'int';
|
||||
}
|
||||
|
||||
if ($node instanceof Array_) {
|
||||
return 'mixed[]';
|
||||
}
|
||||
|
||||
if ($node instanceof DNumber) {
|
||||
return 'float';
|
||||
}
|
||||
|
||||
if ($this->nodeTypeAnalyzer->isStringType($node)) {
|
||||
return 'string';
|
||||
}
|
||||
|
||||
if ($this->constFetchMaintainer->isBool($node)) {
|
||||
return 'bool';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
@ -209,6 +209,8 @@ final class DocBlockAnalyzer
|
||||
|
||||
public function addVarTag(Node $node, string $type): void
|
||||
{
|
||||
// there might be no phpdoc at all
|
||||
if ($node->getDocComment()) {
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
$phpDocNode = $phpDocInfo->getPhpDocNode();
|
||||
|
||||
@ -216,6 +218,11 @@ final class DocBlockAnalyzer
|
||||
$phpDocNode->children[] = new PhpDocTagNode('@var', $varTagValueNode);
|
||||
|
||||
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
|
||||
} else {
|
||||
// create completely new docblock
|
||||
$varDocComment = sprintf("/**\n * @var %s\n */", $type);
|
||||
$node->setDocComment(new Doc($varDocComment));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,165 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Php\Rector\Property;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\NodeTypeResolver\Node\NodeToStringTypeResolver;
|
||||
use Rector\NodeTypeResolver\NodeTypeAnalyzer;
|
||||
use Rector\NodeTypeResolver\Php\VarTypeInfo;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockAnalyzer;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
final class CompleteVarDocTypePropertyRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var DocBlockAnalyzer
|
||||
*/
|
||||
private $docBlockAnalyzer;
|
||||
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
/**
|
||||
* @var NodeTypeAnalyzer
|
||||
*/
|
||||
private $nodeTypeAnalyzer;
|
||||
|
||||
/**
|
||||
* @var NodeToStringTypeResolver
|
||||
*/
|
||||
private $nodeToStringTypeResolver;
|
||||
|
||||
public function __construct(
|
||||
DocBlockAnalyzer $docBlockAnalyzer,
|
||||
BetterNodeFinder $betterNodeFinder,
|
||||
NodeTypeAnalyzer $nodeTypeAnalyzer,
|
||||
NodeToStringTypeResolver $nodeToStringTypeResolver
|
||||
) {
|
||||
$this->docBlockAnalyzer = $docBlockAnalyzer;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->nodeTypeAnalyzer = $nodeTypeAnalyzer;
|
||||
$this->nodeToStringTypeResolver = $nodeToStringTypeResolver;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Complete property `@var` annotations for missing one, yet known.', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
private $eventDispatcher;
|
||||
|
||||
public function __construct(EventDispatcher $eventDispatcher)
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
/**
|
||||
* @var EventDispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
public function __construct(EventDispatcher $eventDispatcher)
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Property::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Property $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$varTypeInfo = $this->docBlockAnalyzer->getVarTypeInfo($node);
|
||||
if ($varTypeInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$varTypeInfo = $this->resolveStaticVarTypeInfo($node);
|
||||
if ($varTypeInfo === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($varTypeInfo->getType() === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->docBlockAnalyzer->addVarTag($node, $varTypeInfo->getType());
|
||||
|
||||
$node->setAttribute(Attribute::ORIGINAL_NODE, null);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo extract
|
||||
* Based on static analysis of code, looking for property assigns
|
||||
*/
|
||||
private function resolveStaticVarTypeInfo(Property $propertyNode): ?VarTypeInfo
|
||||
{
|
||||
$types = [];
|
||||
|
||||
$propertyDefault = $propertyNode->props[0]->default;
|
||||
if ($propertyDefault) {
|
||||
$types[] = $this->nodeToStringTypeResolver->resolver($propertyDefault);
|
||||
}
|
||||
|
||||
/** @var Class_ $classNode */
|
||||
$classNode = $propertyNode->getAttribute(Attribute::CLASS_NODE);
|
||||
|
||||
$propertyName = $this->getName($propertyNode);
|
||||
|
||||
/** @var Assign[] $propertyAssignNodes */
|
||||
$propertyAssignNodes = $this->betterNodeFinder->find([$classNode], function (Node $node) use (
|
||||
$propertyName
|
||||
): bool {
|
||||
if ($node instanceof Assign) {
|
||||
if ($node->var instanceof PropertyFetch) {
|
||||
// is property match
|
||||
return $this->isName($node->var, $propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
foreach ($propertyAssignNodes as $propertyAssignNode) {
|
||||
$types = array_merge(
|
||||
$types,
|
||||
$this->nodeTypeAnalyzer->resolveSingleTypeToStrings($propertyAssignNode->expr)
|
||||
);
|
||||
}
|
||||
|
||||
$types = array_filter($types);
|
||||
|
||||
return new VarTypeInfo($types);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Php\Tests\Rector\Property\CompleteVarDocTypePropertyRector;
|
||||
|
||||
use Rector\Php\Rector\Property\CompleteVarDocTypePropertyRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class CompleteVarDocTypePropertyRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/property_assign.php.inc',
|
||||
__DIR__ . '/Fixture/default_value.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return CompleteVarDocTypePropertyRector::class;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Php\Tests\Rector\Property\CompleteVarDocTypePropertyRector\Fixture;
|
||||
|
||||
final class DefaultValue
|
||||
{
|
||||
private $number = 5;
|
||||
private $maybe = false;
|
||||
private $dreams = [];
|
||||
private $name = 'John';
|
||||
private $longName = 'Elton' . 'John';
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Php\Tests\Rector\Property\CompleteVarDocTypePropertyRector\Fixture;
|
||||
|
||||
final class DefaultValue
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $number = 5;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $maybe = false;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $dreams = [];
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name = 'John';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $longName = 'Elton' . 'John';
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Php\Tests\Rector\Property\CompleteVarDocTypePropertyRector\Fixture;
|
||||
|
||||
final class PropertyAssign
|
||||
{
|
||||
private $eventDispatcher;
|
||||
|
||||
public function __construct(\EventDispatcher $eventDispatcher)
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Php\Tests\Rector\Property\CompleteVarDocTypePropertyRector\Fixture;
|
||||
|
||||
final class PropertyAssign
|
||||
{
|
||||
/**
|
||||
* @var \EventDispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
public function __construct(\EventDispatcher $eventDispatcher)
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -29,6 +29,7 @@ final class FunctionLikeMaintainer
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo extract
|
||||
* Based on static analysis of code, looking for return types
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user