doc block manipulator refactoring to object

This commit is contained in:
TomasVotruba 2020-01-29 14:36:09 +01:00
parent 9f7030c5ce
commit b32ab6c01e
63 changed files with 689 additions and 351 deletions

View File

@ -38,6 +38,7 @@ expectedArguments(
\Rector\NodeTypeResolver\Node\AttributeKey::START_TOKEN_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE,
\Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE,
\Rector\NodeTypeResolver\Node\AttributeKey::PHP_DOC_INFO,
);
expectedArguments(
@ -61,4 +62,5 @@ expectedArguments(
\Rector\NodeTypeResolver\Node\AttributeKey::START_TOKEN_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE,
\Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE,
\Rector\NodeTypeResolver\Node\AttributeKey::PHP_DOC_INFO,
);

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Rector\BetterPhpDocParser\Attributes\Attribute;
use Rector\BetterPhpDocParser\ValueObject\StartEndValueObject;
final class Attribute
{
/**
@ -12,9 +14,10 @@ final class Attribute
public const HAS_DESCRIPTION_WITH_ORIGINAL_SPACES = 'has_description_with_restored_spaces';
/**
* @experiment
* @var string
*/
public const PHP_DOC_NODE_INFO = 'php_doc_node_info';
public const START_END = StartEndValueObject::class;
/**
* @var string

View File

@ -6,6 +6,7 @@ namespace Rector\BetterPhpDocParser\PhpDocInfo;
use Nette\Utils\Strings;
use PhpParser\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
@ -16,7 +17,9 @@ use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareReturnTagValueNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareVarTagValueNode;
use Rector\BetterPhpDocParser\Annotation\AnnotationNaming;
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\SpacelessPhpDocTagNode;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\BetterPhpDocParser\PhpDocNode\AbstractTagValueNode;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\StaticTypeMapper;
@ -78,6 +81,17 @@ final class PhpDocInfo
return $this->originalContent;
}
public function addPhpDocTagNode(PhpDocChildNode $phpDocChildNode): void
{
$this->phpDocNode->children[] = $phpDocChildNode;
}
public function addTagValueNodeWithShortName(AbstractTagValueNode $tagValueNode): void
{
$spacelessPhpDocTagNode = new SpacelessPhpDocTagNode($tagValueNode::SHORT_NAME, $tagValueNode);
$this->addPhpDocTagNode($spacelessPhpDocTagNode);
}
public function getPhpDocNode(): AttributeAwarePhpDocNode
{
return $this->phpDocNode;

View File

@ -12,8 +12,9 @@ use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\ValueObject\StartEndValueObject;
use Rector\Configuration\CurrentNodeProvider;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\StaticTypeMapper;
final class PhpDocInfoFactory
@ -38,11 +39,6 @@ final class PhpDocInfoFactory
*/
private $staticTypeMapper;
/**
* @var PhpDocInfo[]
*/
private $phpDocInfoByObjectHash = [];
public function __construct(
PhpDocParser $phpDocParser,
Lexer $lexer,
@ -57,41 +53,16 @@ final class PhpDocInfoFactory
public function createFromNode(Node $node): PhpDocInfo
{
$hash = $this->createUniqueDocNodeHash($node);
if (isset($this->phpDocInfoByObjectHash[$hash])) {
return $this->phpDocInfoByObjectHash[$hash];
if ($node->hasAttribute(AttributeKey::PHP_DOC_INFO)) {
return $node->getAttribute(AttributeKey::PHP_DOC_INFO);
}
/** needed for @see PhpDocNodeFactoryInterface */
$this->currentNodeProvider->setNode($node);
$content = $node->getDocComment()->getText();
$tokens = $this->lexer->tokenize($content);
$tokenIterator = new TokenIterator($tokens);
/** @var AttributeAwarePhpDocNode $phpDocNode */
$phpDocNode = $this->phpDocParser->parse($tokenIterator);
$phpDocNode = $this->setPositionOfLastToken($phpDocNode);
$phpDocInfo = new PhpDocInfo($phpDocNode, $tokens, $content, $this->staticTypeMapper, $node);
$this->phpDocInfoByObjectHash[$hash] = $phpDocInfo;
$phpDocInfo = $this->createPhpDocInfo($node);
$node->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo);
return $phpDocInfo;
}
private function createUniqueDocNodeHash(Node $node): string
{
$this->ensureNodeHasDocComment($node);
$objectHash = spl_object_hash($node);
$docCommentHash = spl_object_hash($node->getDocComment());
$docCommentContentHash = sha1($node->getDocComment()->getText());
return $objectHash . $docCommentHash . $docCommentContentHash;
}
/**
* Needed for printing
*/
@ -106,20 +77,30 @@ final class PhpDocInfoFactory
/** @var AttributeAwareNodeInterface $lastChildNode */
$lastChildNode = array_pop($phpDocChildNodes);
$phpDocNodeInfo = $lastChildNode->getAttribute(Attribute::PHP_DOC_NODE_INFO);
if ($phpDocNodeInfo !== null) {
$attributeAwarePhpDocNode->setAttribute(Attribute::LAST_TOKEN_POSITION, $phpDocNodeInfo->getEnd());
/** @var StartEndValueObject $startEndValueObject */
$startEndValueObject = $lastChildNode->getAttribute(Attribute::START_END);
if ($startEndValueObject !== null) {
$attributeAwarePhpDocNode->setAttribute(Attribute::LAST_TOKEN_POSITION, $startEndValueObject->getEnd());
}
return $attributeAwarePhpDocNode;
}
private function ensureNodeHasDocComment(Node $node): void
private function createPhpDocInfo(Node $node): PhpDocInfo
{
if ($node->getDocComment() !== null) {
return;
}
/** needed for @see PhpDocNodeFactoryInterface */
$this->currentNodeProvider->setNode($node);
throw new ShouldNotHappenException(sprintf('"%s" is missing a DocComment node', get_class($node)));
$content = $node->getDocComment()->getText();
$tokens = $this->lexer->tokenize($content);
$tokenIterator = new TokenIterator($tokens);
/** @var AttributeAwarePhpDocNode $phpDocNode */
$phpDocNode = $this->phpDocParser->parse($tokenIterator);
$phpDocNode = $this->setPositionOfLastToken($phpDocNode);
return new PhpDocInfo($phpDocNode, $tokens, $content, $this->staticTypeMapper, $node);
}
}

View File

@ -31,6 +31,11 @@ abstract class AbstractTagValueNode implements AttributeAwareNodeInterface, PhpD
*/
protected $hasNewlineAfterOpening = false;
/**
* @var string|null
*/
protected $originalContent;
/**
* @param mixed[] $item
*/
@ -60,6 +65,10 @@ abstract class AbstractTagValueNode implements AttributeAwareNodeInterface, PhpD
}
if ($contentItems === []) {
if ($this->originalContent !== null && Strings::endsWith($this->originalContent, '()')) {
return '()';
}
return '';
}
@ -131,6 +140,7 @@ abstract class AbstractTagValueNode implements AttributeAwareNodeInterface, PhpD
return;
}
$this->originalContent = $originalContent;
$this->orderedVisibleItems = ArrayItemStaticHelper::resolveAnnotationItemsOrder($originalContent);
$this->hasNewlineAfterOpening = (bool) Strings::match($originalContent, '#^\(\s+#m');
$this->hasNewlineBeforeClosing = (bool) Strings::match($originalContent, '#\s+\)$#m');

View File

@ -18,9 +18,10 @@ final class InheritanceTypeTagValueNode extends AbstractDoctrineTagValueNode
*/
private $value;
public function __construct(?string $value)
public function __construct(?string $value, ?string $originalContent)
{
$this->value = $value;
$this->resolveOriginalContentSpacingAndOrder($originalContent);
}
public function __toString(): string
@ -29,6 +30,10 @@ final class InheritanceTypeTagValueNode extends AbstractDoctrineTagValueNode
return '';
}
return '("' . $this->value . '")';
if ($this->originalContent && ! in_array('value', (array) $this->orderedVisibleItems, true)) {
return '("' . $this->value . '")';
}
return '(value="' . $this->value . '")';
}
}

View File

@ -110,7 +110,11 @@ final class TableTagValueNode extends AbstractDoctrineTagValueNode
$contentItems = [];
if ($this->name !== null) {
$contentItems['name'] = sprintf('name="%s"', $this->name);
if ($this->originalContent !== null && ! in_array('name', (array) $this->orderedVisibleItems, true)) {
$contentItems[] = '"' . $this->name . '"';
} else {
$contentItems['name'] = sprintf('name="%s"', $this->name);
}
}
if ($this->schema !== null) {

View File

@ -83,10 +83,6 @@ final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode implemen
{
$contentItems = [];
if ($this->nullable !== null) {
$contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
}
if ($this->name) {
$contentItems['name'] = sprintf('name="%s"', $this->name);
}
@ -95,7 +91,12 @@ final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode implemen
$contentItems['referencedColumnName'] = sprintf('referencedColumnName="%s"', $this->referencedColumnName);
}
if ($this->unique !== null) {
if ($this->nullable !== null) {
$contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
}
// skip default value
if ($this->unique !== null && $this->unique) {
$contentItems['unique'] = sprintf('unique=%s', $this->unique ? 'true' : 'false');
}
@ -134,13 +135,13 @@ final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode implemen
return $this->nullable;
}
public function changeName(string $newName): void
{
$this->name = $newName;
}
public function getTag(): ?string
{
return $this->tag ?: self::SHORT_NAME;
}
public function getUnique(): ?bool
{
return $this->unique;
}
}

View File

@ -46,14 +46,15 @@ final class JMSInjectTagValueNode extends AbstractTagValueNode
$itemContents = [];
if ($this->serviceName) {
$itemContents[] = $this->serviceName;
$itemContents[] = '"' . $this->serviceName . '"';
}
if ($this->required !== null) {
$itemContents[] = sprintf('required=%s', $this->required ? 'true' : 'false');
}
if ($this->strict !== null) {
// skip default
if (! $this->strict) {
$itemContents[] = sprintf('strict=%s', $this->strict ? 'true' : 'false');
}
@ -61,9 +62,7 @@ final class JMSInjectTagValueNode extends AbstractTagValueNode
return '';
}
$stringContent = implode(', ', $itemContents);
return '(' . $stringContent . ')';
return $this->printContentItems($itemContents);
}
public function getServiceName(): ?string

View File

@ -32,6 +32,8 @@ final class InheritanceTypePhpDocNodeFactory extends AbstractPhpDocNodeFactory
return null;
}
return new InheritanceTypeTagValueNode($inheritanceType->value);
$annotationContent = $this->resolveContentFromTokenIterator($tokenIterator);
return new InheritanceTypeTagValueNode($inheritanceType->value, $annotationContent);
}
}

View File

@ -206,7 +206,7 @@ final class BetterPhpDocParser extends PhpDocParser
$startEndValueObject = new StartEndValueObject($tokenStart, $tokenEnd);
$attributeAwareNode = $this->attributeAwareNodeFactory->createFromNode($phpDocNode);
$attributeAwareNode->setAttribute(Attribute::PHP_DOC_NODE_INFO, $startEndValueObject);
$attributeAwareNode->setAttribute(Attribute::START_END, $startEndValueObject);
$possibleMultilineText = $this->multilineSpaceFormatPreserver->resolveCurrentPhpDocNodeText(
$attributeAwareNode

View File

@ -113,7 +113,15 @@ final class PhpDocInfoPrinter
$output .= $this->printNode($phpDocChildNode, null, $i + 1, $nodeCount, $shouldSkipEmptyLinesAbove);
}
return $this->printEnd($output);
$output = $this->printEnd($output);
// @see
// fix missing start
if (! Strings::match($output, '#^(\/\/|\/\*\*|\/\*)#') && $output) {
$output = '/**' . $output;
}
return $output;
}
private function isPhpDocNodeEmpty(PhpDocNode $phpDocNode): bool
@ -145,12 +153,11 @@ final class PhpDocInfoPrinter
$output = '';
/** @var StartEndValueObject|null $startEndValueObject */
$startEndValueObject = $attributeAwareNode->getAttribute(Attribute::PHP_DOC_NODE_INFO) ?: $startEndValueObject;
$startEndValueObject = $attributeAwareNode->getAttribute(Attribute::START_END) ?: $startEndValueObject;
$attributeAwareNode = $this->multilineSpaceFormatPreserver->fixMultilineDescriptions($attributeAwareNode);
if ($startEndValueObject !== null) {
$isLastToken = ($nodeCount === $i);
$output = $this->addTokensFromTo(
$output,
$this->currentTokenPosition,
@ -271,7 +278,7 @@ final class PhpDocInfoPrinter
);
foreach ($removedNodes as $removedNode) {
$removedPhpDocNodeInfo = $removedNode->getAttribute(Attribute::PHP_DOC_NODE_INFO);
$removedPhpDocNodeInfo = $removedNode->getAttribute(Attribute::START_END);
// change start position to start of the line, so the whole line is removed
$seekPosition = $removedPhpDocNodeInfo->getStart();

View File

@ -1,6 +1,6 @@
/**
* @ORM\Entity(readOnly=true, repositoryClass="Rector\BetterPhpDocParser\Tests\PhpDocParser\OrmTagParser\Class_\Source\ExistingRepositoryClass")
* @ORM\Entity
* @ORM\Entity
* @ORM\Entity()
* @ORM\Table(name="answer")
*/

View File

@ -96,6 +96,7 @@ PHP
/** @var string $class */
$class = $this->getName($node);
// properties are accessed via magic, nothing we can do
if (method_exists($class, '__set') || method_exists($class, '__get')) {
return null;

View File

@ -18,7 +18,7 @@ use Rector\RectorDefinition\RectorDefinition;
final class FunctionCallToConstantRector extends AbstractRector
{
/**
* @var string[]string
* @var string[]
*/
private $functionsToConstants = [];

View File

@ -29,7 +29,7 @@ use Rector\RectorDefinition\RectorDefinition;
final class VersionCompareFuncCallToConstantRector extends AbstractRector
{
/**
* @var string[]string
* @var string[]
*/
private $operatorToComparison = [
'=' => Identical::class,

View File

@ -13,7 +13,6 @@ final class SkipAlreadyHas
* - "[package-name] "Message => package-name
* - "[aliased-package-name] "Message => aliased-package-name
* - "[Aliased\PackageName] "Message => Aliased\PackageName
* - "[Aliased\PackageName] "Message => Aliased\PackageName
*/
public const PACKAGE_NAME_PATTERN = '#\[(?<package>[-\w\\\\]+)\]( ){1,}#';
}

View File

@ -3,7 +3,6 @@
namespace Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Routing\Annotation\Route;
/**
* @ORM\Table("Table_Name")
@ -12,13 +11,6 @@ use Symfony\Component\Routing\Annotation\Route;
*/
class ShouldKeepAllDocBlocksAnnotationsParameters
{
/**
* @Route(
* "/{arg1}/{arg2}",
* defaults={"arg1"=null, "arg2"=""},
* requirements={"arg1"="\d+", "arg2"=".*"}
* )
*/
public function nothing(): void
{
}

View File

@ -0,0 +1,19 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture;
use Symfony\Component\Routing\Annotation\Route;
class SymfonyRoute
{
/**
* @Route(
* "/{arg1}/{arg2}",
* defaults={"arg1"=null, "arg2"=""},
* requirements={"arg1"="\d+", "arg2"=".*"}
* )
*/
public function nothing(): void
{
}
}

View File

@ -13,9 +13,11 @@ use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\Contract\Doctrine\DoctrineRelationTagValueNodeInterface;
use Rector\BetterPhpDocParser\Contract\Doctrine\InversedByNodeInterface;
use Rector\BetterPhpDocParser\Contract\Doctrine\MappedByNodeInterface;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocNode\Doctrine\Class_\EntityTagValueNode;
use Rector\BetterPhpDocParser\PhpDocNode\Doctrine\Class_\InheritanceTypeTagValueNode;
use Rector\Doctrine\PhpDocParser\DoctrineDocBlockResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\PhpParser\Node\Resolver\NameResolver;
@ -56,12 +58,12 @@ final class DoctrineEntityManipulator
public function resolveOtherProperty(Property $property): ?string
{
if ($property->getDocComment() === null) {
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return null;
}
$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($property);
$relationTagValueNode = $phpDocInfo->getByType(DoctrineRelationTagValueNodeInterface::class);
if ($relationTagValueNode === null) {
return null;
@ -103,12 +105,11 @@ final class DoctrineEntityManipulator
public function removeMappedByOrInversedByFromProperty(Property $property): void
{
$doc = $property->getDocComment();
if ($doc === null) {
return;
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return null;
}
$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($property);
$relationTagValueNode = $phpDocInfo->getByType(DoctrineRelationTagValueNodeInterface::class);
$shouldUpdate = false;
@ -125,8 +126,6 @@ final class DoctrineEntityManipulator
if (! $shouldUpdate) {
return;
}
$this->docBlockManipulator->updateNodeWithPhpDocInfo($property, $phpDocInfo);
}
/**

View File

@ -268,9 +268,15 @@ PHP
VariableNodeUseInfo $nodeByTypeAndPosition
): bool {
// this node was just used, skip to next one
return $previousNode !== null && ($previousNode->isType(
VariableNodeUseInfo::TYPE_ASSIGN
) && $nodeByTypeAndPosition->isType(VariableNodeUseInfo::TYPE_USE));
if ($previousNode === null) {
return false;
}
if (! $previousNode->isType(VariableNodeUseInfo::TYPE_ASSIGN)) {
return false;
}
return $nodeByTypeAndPosition->isType(VariableNodeUseInfo::TYPE_USE);
}
private function shouldRemoveAssignNode(
@ -301,7 +307,7 @@ PHP
$isVariableAssigned = (bool) $this->betterNodeFinder->findFirst($assignNode->expr, function (Node $node) use (
$nodeByTypeAndPosition
): bool {
return $this->areNodesEqual($node, $nodeByTypeAndPosition->getVariableNode());
return $this->areNodesWithoutCommentsEqual($node, $nodeByTypeAndPosition->getVariableNode());
});
return ! $isVariableAssigned;

View File

@ -66,6 +66,7 @@ PHP
}
$isDeadAfterReturn = false;
$isDeadAfterReturnRemoved = false;
foreach ($node->stmts as $key => $stmt) {
if ($isDeadAfterReturn) {
@ -75,6 +76,7 @@ PHP
}
unset($node->stmts[$key]);
$isDeadAfterReturnRemoved = true;
}
if ($stmt instanceof Return_) {
@ -83,6 +85,10 @@ PHP
}
}
if ($isDeadAfterReturnRemoved === false) {
return null;
}
return $node;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveOverriddenValuesRector\Fixture;
use Nette\Utils\Strings;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Util\RectorStrings;
class KeepPresenterName
{
public function resolvePathFromClassAndMethodNodes(Class_ $classNode, ClassMethod $classMethod): string
{
/** @var string $presenterName */
$presenterName = $this->getName($classNode);
/** @var string $presenterPart */
$presenterPart = Strings::after($presenterName, '\\', -1);
/** @var string $presenterPart */
$presenterPart = Strings::substring($presenterPart, 0, -Strings::length('Presenter'));
$presenterPart = RectorStrings::camelCaseToDashes($presenterPart);
$match = (array) Strings::match($this->getName($classMethod), '#^(action|render)(?<short_action_name>.*?$)#sm');
$actionPart = lcfirst($match['short_action_name']);
return $presenterPart . '/' . $actionPart;
}
}

View File

@ -2,53 +2,125 @@
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveOverriddenValuesRector\Fixture;
final class ReferenceUse
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Return_;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpParser\Node\Manipulator\IfManipulator;
use Rector\PhpParser\Node\Manipulator\StmtsManipulator;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see https://engineering.helpscout.com/reducing-complexity-with-guard-clauses-in-php-and-javascript-74600fd865c7
*
* @see \Rector\SOLID\Tests\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector\ChangeIfElseValueAssignToEarlyReturnRectorTest
*/
final class ChangeIfElseValueAssignToEarlyReturnRector extends AbstractRector
{
public function run(&$directories)
/**
* @var IfManipulator
*/
private $ifManipulator;
/**
* @var StmtsManipulator
*/
private $stmtsManipulator;
public function __construct(IfManipulator $ifManipulator, StmtsManipulator $stmtsManipulator)
{
$directories = 1;
$directories = 4;
$directories = 5;
$this->ifManipulator = $ifManipulator;
$this->stmtsManipulator = $stmtsManipulator;
}
public function plusRun(&$directories)
public function getDefinition(): RectorDefinition
{
$directories = 1;
$directories += 4;
}
return new RectorDefinition('Change if/else value to early return', [
new CodeSample(
<<<'PHP'
class SomeClass
{
public function run()
{
if ($this->hasDocBlock($tokens, $index)) {
$docToken = $tokens[$this->getDocBlockIndex($tokens, $index)];
} else {
$docToken = null;
}
public function get(&$directories)
{
$directories = 1;
$directories = 4;
return $directories;
return $docToken;
}
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveOverriddenValuesRector\Fixture;
final class ReferenceUse
PHP
,
<<<'PHP'
class SomeClass
{
public function run(&$directories)
public function run()
{
$directories = 5;
}
public function plusRun(&$directories)
{
$directories = 1;
$directories += 4;
}
public function get(&$directories)
{
$directories = 4;
return $directories;
if ($this->hasDocBlock($tokens, $index)) {
return $tokens[$this->getDocBlockIndex($tokens, $index)];
}
return null;
}
}
PHP
?>
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [If_::class];
}
/**
* @param If_ $node
*/
public function refactor(Node $node): ?Node
{
$nextNode = $node->getAttribute(AttributeKey::NEXT_NODE);
if (! $nextNode instanceof Return_) {
return null;
}
if ($nextNode->expr === null) {
return null;
}
if (! $this->ifManipulator->isIfAndElseWithSameVariableAssignAsLastStmts($node, $nextNode->expr)) {
return null;
}
$lastIfStmtKey = array_key_last($node->stmts);
/** @var Assign $assign */
$assign = $this->stmtsManipulator->getUnwrappedLastStmt($node->stmts);
$node->stmts[$lastIfStmtKey] = new Return_($assign->expr);
/** @var Assign $assign */
$assign = $this->stmtsManipulator->getUnwrappedLastStmt($node->else->stmts);
$lastElseStmtKey = array_key_last($node->else->stmts);
$elseStmts = $node->else->stmts;
$elseStmts[$lastElseStmtKey] = new Return_($assign->expr);
$node->else = null;
foreach ($elseStmts as $elseStmt) {
$this->addNodeAfterNode($elseStmt, $node);
}
$this->removeNode($nextNode);
return $node;
}
}

View File

@ -10,7 +10,6 @@ use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\Contract\Doctrine\DoctrineRelationTagValueNodeInterface;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocNode\Doctrine\Class_\EntityTagValueNode;
use Rector\BetterPhpDocParser\PhpDocNode\Doctrine\Property_\ColumnTagValueNode;
use Rector\BetterPhpDocParser\PhpDocNode\Doctrine\Property_\IdTagValueNode;
@ -18,24 +17,17 @@ use Rector\Exception\ShouldNotHappenException;
use Rector\NodeContainer\ParsedNodesByType;
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use ReflectionClass;
final class DoctrineDocBlockResolver
{
/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;
/**
* @var ParsedNodesByType
*/
private $parsedNodesByType;
public function __construct(DocBlockManipulator $docBlockManipulator, ParsedNodesByType $parsedNodesByType)
public function __construct(ParsedNodesByType $parsedNodesByType)
{
$this->docBlockManipulator = $docBlockManipulator;
$this->parsedNodesByType = $parsedNodesByType;
}
@ -45,12 +37,12 @@ final class DoctrineDocBlockResolver
public function isDoctrineEntityClass($class): bool
{
if ($class instanceof Class_) {
$classPhpDocInfo = $this->getPhpDocInfo($class);
if ($classPhpDocInfo === null) {
$phpDocInfo = $class->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return false;
}
return $classPhpDocInfo->hasByType(EntityTagValueNode::class);
return $phpDocInfo->hasByType(EntityTagValueNode::class);
}
if (is_string($class)) {
@ -103,36 +95,36 @@ final class DoctrineDocBlockResolver
public function hasPropertyDoctrineIdTag(Property $property): bool
{
$propertyPhpDocInfo = $this->getPhpDocInfo($property);
if ($propertyPhpDocInfo === null) {
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return false;
}
return $propertyPhpDocInfo->hasByType(IdTagValueNode::class);
return $phpDocInfo->hasByType(IdTagValueNode::class);
}
public function getDoctrineRelationTagValueNode(Property $property): ?DoctrineRelationTagValueNodeInterface
{
$propertyPhpDocInfo = $this->getPhpDocInfo($property);
if ($propertyPhpDocInfo === null) {
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return null;
}
return $propertyPhpDocInfo->getByType(DoctrineRelationTagValueNodeInterface::class);
return $phpDocInfo->getByType(DoctrineRelationTagValueNodeInterface::class);
}
public function isDoctrineProperty(Property $property): bool
{
$propertyPhpDocInfo = $this->getPhpDocInfo($property);
if ($propertyPhpDocInfo === null) {
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return false;
}
if ($propertyPhpDocInfo->hasByType(ColumnTagValueNode::class)) {
if ($phpDocInfo->hasByType(ColumnTagValueNode::class)) {
return true;
}
return $propertyPhpDocInfo->hasByType(DoctrineRelationTagValueNodeInterface::class);
return $phpDocInfo->hasByType(DoctrineRelationTagValueNodeInterface::class);
}
public function isInDoctrineEntityClass(Node $node): bool
@ -145,13 +137,4 @@ final class DoctrineDocBlockResolver
return $this->isDoctrineEntityClass($classNode);
}
private function getPhpDocInfo(Node $node): ?PhpDocInfo
{
if ($node->getDocComment() === null) {
return null;
}
return $this->docBlockManipulator->createPhpDocInfoFromNode($node);
}
}

View File

@ -126,7 +126,7 @@ final class AddUuidMirrorForRelationPropertyRector extends AbstractRector
$propertyWithUuid = clone $property;
// this is needed to keep old property name
$this->updateDocComment($propertyWithUuid);
$this->mirrorPhpDocInfoToUuid($propertyWithUuid);
// name must be changed after the doc comment update, because the reflection annotation needed for update of doc comment
// would miss non existing *Uuid property
@ -152,21 +152,21 @@ final class AddUuidMirrorForRelationPropertyRector extends AbstractRector
return false;
}
private function updateDocComment(Property $property): void
private function mirrorPhpDocInfoToUuid(Property $property): void
{
/** @var PhpDocInfo $propertyPhpDocInfo */
$propertyPhpDocInfo = $this->getPhpDocInfo($property);
$newPropertyPhpDocInfo = clone $propertyPhpDocInfo;
/** @var DoctrineRelationTagValueNodeInterface $doctrineRelationTagValueNode */
$doctrineRelationTagValueNode = $this->getDoctrineRelationTagValueNode($property);
if ($doctrineRelationTagValueNode instanceof ToManyTagNodeInterface) {
$this->refactorToManyPropertyPhpDocInfo($propertyPhpDocInfo, $property);
$this->refactorToManyPropertyPhpDocInfo($newPropertyPhpDocInfo, $property);
} elseif ($doctrineRelationTagValueNode instanceof ToOneTagNodeInterface) {
$this->refactorToOnePropertyPhpDocInfo($propertyPhpDocInfo);
$this->refactorToOnePropertyPhpDocInfo($newPropertyPhpDocInfo);
}
$this->docBlockManipulator->updateNodeWithPhpDocInfo($property, $propertyPhpDocInfo);
}
private function addNewPropertyToCollector(
@ -197,22 +197,28 @@ final class AddUuidMirrorForRelationPropertyRector extends AbstractRector
}
$joinTableTagNode = $this->phpDocTagNodeFactory->createJoinTableTagNode($property);
$propertyPhpDocInfo->getPhpDocNode()->children[] = $joinTableTagNode;
$propertyPhpDocInfo->addPhpDocTagNode($joinTableTagNode);
}
private function refactorToOnePropertyPhpDocInfo(PhpDocInfo $propertyPhpDocInfo): void
{
/** @var JoinColumnTagValueNode $joinColumnTagValueNode */
/** @var JoinColumnTagValueNode|null $joinColumnTagValueNode */
$joinColumnTagValueNode = $propertyPhpDocInfo->getByType(JoinColumnTagValueNode::class);
if ($joinColumnTagValueNode) {
$joinColumnTagValueNode->changeName('');
$joinColumnTagValueNode->changeNullable(true);
$joinColumnTagValueNode->changeReferencedColumnName('uuid');
} else {
$propertyPhpDocInfo->getPhpDocNode()->children[] = $this->phpDocTagNodeFactory->createJoinColumnTagNode(
if ($joinColumnTagValueNode !== null) {
// remove first
$propertyPhpDocInfo->removeByType(JoinColumnTagValueNode::class);
$mirrorJoinColumnTagValueNode = new JoinColumnTagValueNode(
'',
'uuid',
$joinColumnTagValueNode->getUnique(),
true
);
} else {
$mirrorJoinColumnTagValueNode = $this->phpDocTagNodeFactory->createJoinColumnTagNode(true);
}
$propertyPhpDocInfo->addTagValueNodeWithShortName($mirrorJoinColumnTagValueNode);
}
}

View File

@ -72,7 +72,6 @@ PHP
$doctrineEntityTag->removeRepositoryClass();
// save the entity tag
$this->docBlockManipulator->updateNodeWithPhpDocInfo($node, $phpDocInfo);
return $node;
}

View File

@ -48,23 +48,22 @@ final class AddUuidAnnotationsToIdPropertyRector extends AbstractRector
return null;
}
$this->changeVarToUuidInterface($node);
/** @var PhpDocInfo $phpDocInfo */
$phpDocInfo = $this->getPhpDocInfo($node);
$this->changeVarToUuidInterface($node);
$phpDocInfo->removeByType(GeneratedValueTagValueNode::class);
$this->changeColumnTypeToUuidBinary($phpDocInfo);
$this->changeSerializerTypeToString($phpDocInfo);
$this->docBlockManipulator->updateNodeWithPhpDocInfo($node, $phpDocInfo);
return $node;
}
private function changeVarToUuidInterface(Property $property): void
{
$uuidObjectType = new FullyQualifiedObjectType(UuidInterface::class);
$this->docBlockManipulator->changeVarTag($property, $uuidObjectType);
}

View File

@ -58,8 +58,8 @@ class ManyToManyWithExtraName
private $itemRole;
/**
* @ORM\ManyToOne(targetEntity="Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Fixture\BarEntity")
* @ORM\JoinColumn(referencedColumnName="uuid", nullable=true)
* @Serializer\Type("Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Fixture\BarEntity")
* @ORM\JoinColumn(referencedColumnName="uuid", nullable=true)
*/
private $itemRoleUuid;
}

View File

@ -101,7 +101,6 @@ PHP
}
$classPhpDocInfo->removeByType(LoggableTagValueNode::class);
$this->docBlockManipulator->updateNodeWithPhpDocInfo($node, $classPhpDocInfo);
// remove tag from properties
$this->removeVersionedTagFromProperties($node);
@ -126,7 +125,6 @@ PHP
}
$propertyPhpDocInfo->removeByType(VersionedTagValueNode::class);
$this->docBlockManipulator->updateNodeWithPhpDocInfo($property, $propertyPhpDocInfo);
}
}
}

View File

@ -110,7 +110,6 @@ PHP
$node->implements[] = new FullyQualified('Knp\DoctrineBehaviors\Contract\Entity\SoftDeletableInterface');
$classPhpDocInfo->removeByType(SoftDeleteableTagValueNode::class);
$this->docBlockManipulator->updateNodeWithPhpDocInfo($node, $classPhpDocInfo);
return $node;
}

View File

@ -261,7 +261,7 @@ PHP
foreach ($translatedPropertyToPhpDocInfos as $translatedPropertyName => $translatedPhpDocInfo) {
$property = $this->nodeFactory->createPrivateProperty($translatedPropertyName);
$this->docBlockManipulator->updateNodeWithPhpDocInfo($property, $translatedPhpDocInfo);
$property->setAttribute(AttributeKey::PHP_DOC_INFO, $translatedPhpDocInfo);
$class->stmts[] = $property;
}

View File

@ -148,7 +148,6 @@ PHP
// we're in a tree entity
$classPhpDocInfo->removeByType(TreeTagValueNode::class);
$this->docBlockManipulator->updateNodeWithPhpDocInfo($node, $classPhpDocInfo);
$node->implements[] = new FullyQualified('Knp\DoctrineBehaviors\Contract\Entity\TreeNodeInterface');
$this->classManipulator->addAsFirstTrait($node, 'Knp\DoctrineBehaviors\Model\Tree\TreeNodeTrait');

View File

@ -335,8 +335,10 @@ PHP
{
/** @var string $presenterName */
$presenterName = $this->getName($classNode);
/** @var string $presenterPart */
$presenterPart = Strings::after($presenterName, '\\', -1);
/** @var string $presenterPart */
$presenterPart = Strings::substring($presenterPart, 0, -Strings::length('Presenter'));
$presenterPart = RectorStrings::camelCaseToDashes($presenterPart);

View File

@ -4,12 +4,20 @@ declare(strict_types=1);
namespace Rector\NodeTypeResolver\Node;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Namespace_;
use PHPStan\Analyser\Scope;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Symplify\SmartFileSystem\SmartFileInfo;
final class AttributeKey
{
/**
* @var string
*/
public const SCOPE = 'scope';
public const SCOPE = Scope::class;
/**
* @var string
@ -19,7 +27,7 @@ final class AttributeKey
/**
* @var string
*/
public const NAMESPACE_NODE = 'namespaceNode';
public const NAMESPACE_NODE = Namespace_::class;
/**
* @var string
@ -35,7 +43,7 @@ final class AttributeKey
* @todo split Class node, interface node and trait node, to be compatible with other SpecificNode|null, values
* @var string
*/
public const CLASS_NODE = 'classNode';
public const CLASS_NODE = ClassLike::class;
/**
* @var string
@ -50,7 +58,7 @@ final class AttributeKey
/**
* @var string
*/
public const METHOD_NODE = 'methodNode';
public const METHOD_NODE = ClassMethod::class;
/**
* Internal php-parser name.
@ -96,7 +104,7 @@ final class AttributeKey
/**
* @var string
*/
public const FILE_INFO = 'fileInfo';
public const FILE_INFO = SmartFileInfo::class;
/**
* @var string
@ -117,5 +125,10 @@ final class AttributeKey
/**
* @var string
*/
public const FUNCTION_NODE = 'function_node';
public const FUNCTION_NODE = Function_::class;
/**
* @var string
*/
public const PHP_DOC_INFO = PhpDocInfo::class;
}

View File

@ -14,6 +14,7 @@ use Rector\NodeTypeResolver\NodeVisitor\FunctionMethodAndClassNodeVisitor;
use Rector\NodeTypeResolver\NodeVisitor\NamespaceNodeVisitor;
use Rector\NodeTypeResolver\NodeVisitor\NodeCollectorNodeVisitor;
use Rector\NodeTypeResolver\NodeVisitor\ParentAndNextNodeVisitor;
use Rector\NodeTypeResolver\NodeVisitor\PhpDocInfoNodeVisitor;
use Rector\NodeTypeResolver\NodeVisitor\StatementNodeVisitor;
use Rector\NodeTypeResolver\PHPStan\Scope\NodeScopeResolver;
@ -64,6 +65,11 @@ final class NodeScopeAndMetadataDecorator
*/
private $configuration;
/**
* @var PhpDocInfoNodeVisitor
*/
private $phpDocInfoNodeVisitor;
public function __construct(
NodeScopeResolver $nodeScopeResolver,
ParentAndNextNodeVisitor $parentAndNextNodeVisitor,
@ -73,6 +79,7 @@ final class NodeScopeAndMetadataDecorator
StatementNodeVisitor $statementNodeVisitor,
FileInfoNodeVisitor $fileInfoNodeVisitor,
NodeCollectorNodeVisitor $nodeCollectorNodeVisitor,
PhpDocInfoNodeVisitor $phpDocInfoNodeVisitor,
Configuration $configuration
) {
$this->nodeScopeResolver = $nodeScopeResolver;
@ -84,6 +91,7 @@ final class NodeScopeAndMetadataDecorator
$this->fileInfoNodeVisitor = $fileInfoNodeVisitor;
$this->nodeCollectorNodeVisitor = $nodeCollectorNodeVisitor;
$this->configuration = $configuration;
$this->phpDocInfoNodeVisitor = $phpDocInfoNodeVisitor;
}
/**
@ -117,6 +125,7 @@ final class NodeScopeAndMetadataDecorator
$nodeTraverser->addVisitor($this->parentAndNextNodeVisitor);
$nodeTraverser->addVisitor($this->functionMethodAndClassNodeVisitor);
$nodeTraverser->addVisitor($this->namespaceNodeVisitor);
$nodeTraverser->addVisitor($this->phpDocInfoNodeVisitor);
$nodes = $nodeTraverser->traverse($nodes);

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
final class PhpDocInfoNodeVisitor extends NodeVisitorAbstract
{
/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;
public function __construct(DocBlockManipulator $docBlockManipulator)
{
$this->docBlockManipulator = $docBlockManipulator;
}
/**
* @return int|Node|void|null
*/
public function enterNode(Node $node)
{
if (empty($node->getComments())) {
$node->setAttribute(AttributeKey::PHP_DOC_INFO, null);
return;
}
$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($node);
$node->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo);
return $node;
}
}

View File

@ -61,4 +61,9 @@ final class TypeHasher
return $this->phpStanStaticTypeMapper->mapToDocString($type);
}
public function areTypesEqual(Type $firstType, Type $secondType): bool
{
return $this->createTypeHash($firstType) === $this->createTypeHash($secondType);
}
}

View File

@ -151,7 +151,6 @@ final class DocBlockManipulator
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
$phpDocNode = $phpDocInfo->getPhpDocNode();
$phpDocNode->children[] = $phpDocChildNode;
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
} else {
$phpDocNode = new AttributeAwarePhpDocNode([$phpDocChildNode]);
$node->setDocComment(new Doc($phpDocNode->__toString()));
@ -164,16 +163,18 @@ final class DocBlockManipulator
$this->addTag($node, $spacelessPhpDocTagNode);
}
public function removeTagFromNode(Node $node, string $name, bool $shouldSkipEmptyLinesAbove = false): void
/**
* @deprecated
* Use @see PhpDocInfo::removeByType(x) directly
*/
public function removeTagFromNode(Node $node, string $name): void
{
if ($node->getDocComment() === null) {
return;
}
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
$this->removeTagByName($phpDocInfo, $name);
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo, $shouldSkipEmptyLinesAbove);
}
public function changeType(Node $node, Type $oldType, Type $newType): void
@ -183,16 +184,8 @@ final class DocBlockManipulator
}
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
$hasNodeChanged = $this->docBlockClassRenamer->renamePhpDocType(
$phpDocInfo->getPhpDocNode(),
$oldType,
$newType,
$node
);
if ($hasNodeChanged) {
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
}
$this->docBlockClassRenamer->renamePhpDocType($phpDocInfo->getPhpDocNode(), $oldType, $newType, $node);
}
public function replaceAnnotationInNode(Node $node, string $oldAnnotation, string $newAnnotation): void
@ -203,8 +196,6 @@ final class DocBlockManipulator
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
$this->replaceTagByAnother($phpDocInfo->getPhpDocNode(), $oldAnnotation, $newAnnotation);
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
}
public function getReturnType(Node $node): Type
@ -284,10 +275,6 @@ final class DocBlockManipulator
$phpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
$varTagValueNode->type = $phpDocType;
// update doc :)
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
} else {
$this->addTypeSpecificTag($node, 'var', $newType);
}
@ -314,7 +301,6 @@ final class DocBlockManipulator
$newPHPStanPhpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
$returnTagValueNode->type = $newPHPStanPhpDocType;
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
return;
}
}
@ -412,11 +398,7 @@ final class DocBlockManipulator
}
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
$hasNodeChanged = $this->docBlockNameImporter->importNames($phpDocInfo, $node);
if ($hasNodeChanged) {
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
}
$this->docBlockNameImporter->importNames($phpDocInfo, $node);
}
/**
@ -467,8 +449,6 @@ final class DocBlockManipulator
if (! $this->hasPhpDocChanged) {
return;
}
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
}
/**
@ -502,26 +482,30 @@ final class DocBlockManipulator
return false;
}
public function updateNodeWithPhpDocInfo(
Node $node,
PhpDocInfo $phpDocInfo,
bool $shouldSkipEmptyLinesAbove = false
): bool {
$phpDoc = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo, $shouldSkipEmptyLinesAbove);
if ($phpDoc !== '') {
// no change, don't save it
if ($node->getDocComment() && $node->getDocComment()->getText() === $phpDoc) {
return false;
}
public function updateNodeWithPhpDocInfo(Node $node, bool $shouldSkipEmptyLinesAbove = false): void
{
$phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
$node->setDocComment(new Doc($phpDoc));
return true;
// nothing to change
if ($phpDocInfo === null) {
return;
}
// no comments, null
$node->setAttribute('comments', null);
$phpDoc = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo, $shouldSkipEmptyLinesAbove);
if ($phpDoc === '') {
// no comments, null
$node->setAttribute('comments', null);
return;
}
return true;
// no change, don't save it
// this is needed to prevent short classes override with FQN with same value → people don't like that for some reason
if ($node->getDocComment() && $node->getDocComment()->getText() === $phpDoc) {
return;
}
$node->setDocComment(new Doc($phpDoc));
}
public function getDoctrineFqnTargetEntity(Node $node): ?string
@ -574,10 +558,7 @@ final class DocBlockManipulator
return true;
}
$firstTypeHash = $this->typeHasher->createTypeHash($firstType);
$secondTypeHash = $this->typeHasher->createTypeHash($secondType);
if ($firstTypeHash === $secondTypeHash) {
if ($this->typeHasher->areTypesEqual($firstType, $secondType)) {
return true;
}
@ -604,8 +585,6 @@ final class DocBlockManipulator
$docStringType
), '', '');
$phpDocNode->children[] = new AttributeAwarePhpDocTagNode('@' . $name, $varTagValueNode);
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
} else {
// create completely new docblock
$varDocComment = sprintf("/**\n * @%s %s\n */", $name, $docStringType);

View File

@ -30,7 +30,7 @@ array(
5: RandomProperty
)
attributes: array(
classNode: null
PhpParser\Node\Stmt\ClassLike: null
className: null
methodName: null
)
@ -41,7 +41,7 @@ array(
name: Identifier #3(
name: foo
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\RandomProperty\foo
methodName: null
)
@ -53,7 +53,7 @@ array(
name: Identifier #5(
name: bar
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\RandomProperty\foo
methodName: bar
)
@ -70,32 +70,32 @@ array(
name: Identifier #10(
name: baz
attributes: array(
classNode: Stmt_Class #8
PhpParser\Node\Stmt\ClassLike: Stmt_Class #8
className: null
methodName: baz
)
)
attributes: array(
classNode: Stmt_Class #8
PhpParser\Node\Stmt\ClassLike: Stmt_Class #8
className: null
methodName: baz
)
)
)
attributes: array(
classNode: Stmt_Class #8
PhpParser\Node\Stmt\ClassLike: Stmt_Class #8
className: null
methodName: bar
)
)
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\RandomProperty\foo
methodName: bar
)
)
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\RandomProperty\foo
methodName: bar
)
@ -107,40 +107,40 @@ array(
0: moreCode
)
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\RandomProperty\foo
methodName: baz
)
)
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\RandomProperty\foo
methodName: baz
)
)
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\RandomProperty\foo
methodName: baz
)
)
)
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\RandomProperty\foo
methodName: bar
)
)
)
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\RandomProperty\foo
methodName: null
)
)
)
attributes: array(
classNode: null
PhpParser\Node\Stmt\ClassLike: null
className: null
methodName: null
)

View File

@ -23,7 +23,7 @@ array(
5: Simple
)
attributes: array(
classNode: null
PhpParser\Node\Stmt\ClassLike: null
className: null
methodName: null
)
@ -34,7 +34,7 @@ array(
name: Identifier #3(
name: foo
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\Simple\foo
methodName: null
)
@ -46,27 +46,27 @@ array(
name: Identifier #5(
name: bar
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\Simple\foo
methodName: bar
)
)
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\Simple\foo
methodName: bar
)
)
)
attributes: array(
classNode: Stmt_Class #2
PhpParser\Node\Stmt\ClassLike: Stmt_Class #2
className: Rector\NodeTypeResolver\Tests\NodeVisitor\FunctionMethodAndClassNodeVisitorTest\Simple\foo
methodName: null
)
)
)
attributes: array(
classNode: null
PhpParser\Node\Stmt\ClassLike: null
className: null
methodName: null
)

View File

@ -18,7 +18,9 @@ use PhpParser\Node\Stmt\Static_;
use PhpParser\Node\Stmt\Switch_;
use PhpParser\Node\Stmt\Throw_;
use PhpParser\Node\Stmt\While_;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareVarTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@ -76,12 +78,18 @@ PHP
return null;
}
$nodeContent = $this->print($node);
// clear phpdoc - @see https://regex101.com/r/uwY5KW/1
$nodeContentWithoutPhpDoc = Strings::replace($nodeContent, '#\/\*\*(.*?)*\/#');
// it's there
if (Strings::match($this->print($node), '#' . preg_quote($variableName, '#') . '\b#')) {
if (Strings::match($nodeContentWithoutPhpDoc, '#' . preg_quote($variableName, '#') . '\b#')) {
return null;
}
$this->docBlockManipulator->removeTagFromNode($node, 'var');
/** @var PhpDocInfo $phpDocInfo */
$phpDocInfo = $this->getPhpDocInfo($node);
$phpDocInfo->removeByType(VarTagValueNode::class);
return $node;
}

View File

@ -155,8 +155,6 @@ PHP
$phpDocNode->children[] = new AttributeAwarePhpDocTagNode('@doesNotPerformAssertion', new GenericTagValueNode(
''
));
$this->docBlockManipulator->updateNodeWithPhpDocInfo($classMethod, $phpDocInfo);
}
private function containsAssertCall(ClassMethod $classMethod): bool

View File

@ -165,7 +165,5 @@ PHP
$phpDocInfo = $this->getPhpDocInfo($privateProperty);
$phpDocNode = $phpDocInfo->getPhpDocNode();
$phpDocNode->children[] = new AttributeAwarePhpDocTagNode('@inject', new GenericTagValueNode(''));
$this->docBlockManipulator->updateNodeWithPhpDocInfo($privateProperty, $phpDocInfo);
}
}

View File

@ -139,7 +139,6 @@ PHP
}
$propertyPhpDocInfo->removeByType(VarTagValueNode::class);
$this->docBlockManipulator->updateNodeWithPhpDocInfo($property, $propertyPhpDocInfo);
}
private function isNonBasicArrayType(Property $property, VarTagValueNode $varTagValueNode): bool

View File

@ -101,10 +101,11 @@ PHP
return null;
}
$lastIfStmtKey = array_key_last($node->stmts);
/** @var Assign $assign */
$assign = $this->stmtsManipulator->getUnwrappedLastStmt($node->stmts);
$lastIfStmtKey = array_key_last($node->stmts);
$node->stmts[$lastIfStmtKey] = new Return_($assign->expr);
/** @var Assign $assign */

View File

@ -73,13 +73,13 @@ PHP
*/
public function refactor(Node $node): ?Node
{
$phpDocInfo = $this->getPhpDocInfo($node);
if ($phpDocInfo === null) {
// skip properties
if ($node instanceof Property) {
return null;
}
// skip properties
if ($node instanceof Property) {
$phpDocInfo = $this->getPhpDocInfo($node);
if ($phpDocInfo === null) {
return null;
}
@ -133,6 +133,7 @@ PHP
if (! $node instanceof Expression) {
return false;
}
if (! $node->expr instanceof Assign) {
return false;
}
@ -149,7 +150,7 @@ PHP
private function refactorFreshlyCreatedNode(Node $node, PhpDocInfo $phpDocInfo, Variable $variable): ?Node
{
$node->setAttribute('comments', []);
$node->setAttribute('comments', null);
$type = $phpDocInfo->getVarType();
$assertFuncCall = $this->createFuncCallBasedOnType($type, $variable);
@ -157,7 +158,8 @@ PHP
return null;
}
$this->removeVarAnnotation($variable, $phpDocInfo);
$phpDocInfo->removeByType(VarTagValueNode::class);
$this->addNodeBeforeNode($assertFuncCall, $node);
return $node;
@ -166,7 +168,6 @@ PHP
private function refactorAlreadyCreatedNode(Node $node, PhpDocInfo $phpDocInfo, Variable $variable): ?Node
{
$varTagValue = $phpDocInfo->getVarTagValue();
$phpStanType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType(
$varTagValue->type,
$variable
@ -177,7 +178,6 @@ PHP
return null;
}
$this->removeVarAnnotation($variable, $phpDocInfo);
$this->addNodeAfterNode($assertFuncCall, $node);
return $node;
@ -212,11 +212,4 @@ PHP
return null;
}
private function removeVarAnnotation(Node $node, PhpDocInfo $phpDocInfo): void
{
$phpDocInfo->removeByType(VarTagValueNode::class);
$this->docBlockManipulator->updateNodeWithPhpDocInfo($node, $phpDocInfo);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\StrictCodeQuality\Tests\Rector\Stmt\VarInlineAnnotationToAssertRector\Fixture;
class SingleScalarAssert
{
public function run($value)
{
/** @var int $value */
$value->call();
}
}
?>
-----
<?php
namespace Rector\StrictCodeQuality\Tests\Rector\Stmt\VarInlineAnnotationToAssertRector\Fixture;
class SingleScalarAssert
{
public function run($value)
{
assert(is_int($value));
$value->call();
}
}
?>

View File

@ -102,8 +102,6 @@ PHP
$phpDocInfo->removeTagValueNodeFromNode($symfonyMethodPhpDocTagValueNode);
$this->docBlockManipulator->updateNodeWithPhpDocInfo($node, $phpDocInfo);
return $node;
}
}

View File

@ -7,30 +7,18 @@ namespace Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\TypeDeclaration\Contract\TypeInferer\PropertyTypeInfererInterface;
final class VarDocPropertyTypeInferer implements PropertyTypeInfererInterface
{
/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;
public function __construct(DocBlockManipulator $docBlockManipulator)
{
$this->docBlockManipulator = $docBlockManipulator;
}
public function inferProperty(Property $property): Type
{
if ($property->getDocComment() === null) {
return new MixedType();
}
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($property);
return $phpDocInfo->getVarType();
return $phpDocInfo ? $phpDocInfo->getVarType() : new MixedType();
}
public function getPriority(): int

View File

@ -233,3 +233,6 @@ parameters:
- '#Parameter \#1 \$node of method PHPStan\\Analyser\\Scope\:\:getType\(\) expects PhpParser\\Node\\Expr, PhpParser\\Node given#'
- '#Parameter \#2 \$name of class PhpParser\\Node\\Expr\\MethodCall constructor expects PhpParser\\Node\\Expr\|PhpParser\\Node\\Identifier\|string, string\|null given#'
- '#Ternary operator condition is always false#'
- '#Parameter \#1 \$tagValueNode of method Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\:\:addTagValueNodeWithShortName\(\) expects Rector\\BetterPhpDocParser\\PhpDocNode\\AbstractTagValueNode, PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagNode\|Rector\\BetterPhpDocParser\\PhpDocNode\\Doctrine\\Property_\\JoinColumnTagValueNode given#'

View File

@ -1,21 +1,30 @@
parameters:
sets:
- 'coding-style'
- 'code-quality'
- 'dead-code'
- 'nette-utils-code-quality'
services:
Rector\DeadCode\Rector\ClassMethod\RemoveOverriddenValuesRector: null
Rector\CodingStyle\Rector\Use_\RemoveUnusedAliasRector: null
paths:
- 'src'
- 'packages'
- 'tests'
exclude_paths:
- '/Fixture/'
- '/Source/'
- '/Expected/'
exclude_rectors:
# @todo needs to check for "autoload-dev" local dependency
- 'Rector\Php55\Rector\String_\StringClassNameToClassConstantRector'
- 'Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector'
# Rector\CodeQuality\Rector\If_\RemoveAlwaysTrueConditionSetInConstructorRector: null
# Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector: null
#
#
#parameters:
# sets:
# - 'coding-style'
## - 'code-quality'
# - 'dead-code'
## - 'nette-utils-code-quality'
#
# paths:
# - 'src'
# - 'packages'
# - 'tests'
#
# exclude_paths:
# - '/Fixture/'
# - '/Source/'
# - '/Expected/'
#
# exclude_rectors:
# # @todo needs to check for "autoload-dev" local dependency
# - 'Rector\Php55\Rector\String_\StringClassNameToClassConstantRector'
# - 'Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector'
## - 'Rector\DeadCode\Rector\ClassMethod\RemoveOverriddenValuesRector'

View File

@ -23,10 +23,10 @@ final class AppliedRectorCollector
*/
public function getRectorClasses(SmartFileInfo $smartFileInfo): array
{
if (isset($this->rectorClassesByFile[$smartFileInfo->getRealPath()])) {
return array_unique($this->rectorClassesByFile[$smartFileInfo->getRealPath()]);
if (! isset($this->rectorClassesByFile[$smartFileInfo->getRealPath()])) {
return [];
}
return [];
return array_unique($this->rectorClassesByFile[$smartFileInfo->getRealPath()]);
}
}

View File

@ -21,6 +21,7 @@ use PhpParser\Node\Stmt\Nop;
use PhpParser\Node\Stmt\TraitUse;
use PhpParser\PrettyPrinter\Standard;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Symplify\SmartFileSystem\SmartFileInfo;
/**
@ -34,6 +35,11 @@ final class BetterStandardPrinter extends Standard
*/
private $tabOrSpaceIndentCharacter = ' ';
/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;
/**
* @param mixed[] $options
*/
@ -48,6 +54,14 @@ final class BetterStandardPrinter extends Standard
$this->insertionMap['Expr_Closure->returnType'] = [')', false, ': ', null];
}
/**
* @required
*/
public function autowireBetterStandardPrinter(DocBlockManipulator $docBlockManipulator): void
{
$this->docBlockManipulator = $docBlockManipulator;
}
public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string
{
// detect per print
@ -60,6 +74,17 @@ final class BetterStandardPrinter extends Standard
return Strings::replace($clearContent, '#\<\?php(\s+)\?\>#');
}
/**
* @param Node|Node[]|null $node
*/
public function printWithoutComments($node): string
{
$printerNode = $this->print($node);
$nodeWithoutComments = $this->removeComments($printerNode);
return trim($nodeWithoutComments);
}
/**
* @param Node|Node[]|null $node
*/
@ -73,12 +98,6 @@ final class BetterStandardPrinter extends Standard
return 'UNABLE_TO_PRINT_ENCAPSED_STRING';
}
// remove comments, for value compare
if ($node instanceof Node) {
$node = clone $node;
$node->setAttribute('comments', null);
}
if (! is_array($node)) {
$node = [$node];
}
@ -95,6 +114,15 @@ final class BetterStandardPrinter extends Standard
return $this->print($firstNode) === $this->print($secondNode);
}
/**
* @param Node|Node[]|null $firstNode
* @param Node|Node[]|null $secondNode
*/
public function areNodesWithoutCommentsEqual($firstNode, $secondNode): bool
{
return $this->printWithoutComments($firstNode) === $this->printWithoutComments($secondNode);
}
/**
* This allows to use both spaces and tabs vs. original space-only
*/
@ -150,6 +178,8 @@ final class BetterStandardPrinter extends Standard
// reindex positions for printer
$nodes = array_values($nodes);
$this->moveCommentsFromAttributeObjectToCommentsAttribute($nodes);
$content = parent::pArray($nodes, $origNodes, $pos, $indentAdjustment, $parentNodeType, $subNodeName, $fixup);
if ($content === null) {
@ -191,11 +221,16 @@ final class BetterStandardPrinter extends Standard
}
/**
* Add space after "use ("
* Add space:
* "use("
*
* "use ("
*/
protected function pExpr_Closure(Closure $closure): string
{
return Strings::replace(parent::pExpr_Closure($closure), '#( use)\(#', '$1 (');
$closureContent = parent::pExpr_Closure($closure);
return Strings::replace($closureContent, '#( use)\(#', '$1 (');
}
/**
@ -246,6 +281,16 @@ final class BetterStandardPrinter extends Standard
return parent::pScalar_String($node);
}
/**
* @param Node[] $nodes
*/
protected function pStmts(array $nodes, bool $indent = true): string
{
$this->moveCommentsFromAttributeObjectToCommentsAttribute($nodes);
return parent::pStmts($nodes, $indent);
}
/**
* "...$params) : ReturnType"
*
@ -339,4 +384,25 @@ final class BetterStandardPrinter extends Standard
return false;
}
/**
* @todo decopule
*/
private function moveCommentsFromAttributeObjectToCommentsAttribute(array $nodes): void
{
// move phpdoc from node to "comment" attirbute
foreach ($nodes as $node) {
if (! $node instanceof Node) {
continue;
}
$this->docBlockManipulator->updateNodeWithPhpDocInfo($node);
}
}
private function removeComments(string $printerNode): string
{
$printerNode = Strings::replace($printerNode, '#\/*\*(.*?)\*\/#');
return Strings::replace($printerNode, '#\/\/(.*?)$#m');
}
}

View File

@ -14,9 +14,13 @@ use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\NodeVisitorAbstract;
use PHPStan\Analyser\Scope;
use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter;
use Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector;
use Rector\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector;
use Rector\Commander\CommanderCollector;
use Rector\Contract\PhpParser\Node\CommanderInterface;
use Rector\Contract\Rector\PhpRectorInterface;
use Rector\DeadCode\Rector\FunctionLike\RemoveCodeAfterReturnRector;
use Rector\Exclusion\ExclusionManager;
use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -60,6 +64,11 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
*/
private $currentFileInfoProvider;
/**
* @var PhpDocInfoPrinter
*/
protected $phpDocInfoPrinter;
/**
* Run once in the every end of one processed file
*/
@ -76,7 +85,8 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
BuilderFactory $builderFactory,
ExclusionManager $exclusionManager,
CommanderCollector $commanderCollector,
CurrentFileInfoProvider $currentFileInfoProvider
CurrentFileInfoProvider $currentFileInfoProvider,
PhpDocInfoPrinter $phpDocInfoPrinter
): void {
$this->symfonyStyle = $symfonyStyle;
$this->phpVersionProvider = $phpVersionProvider;
@ -84,6 +94,7 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
$this->exclusionManager = $exclusionManager;
$this->commanderCollector = $commanderCollector;
$this->currentFileInfoProvider = $currentFileInfoProvider;
$this->phpDocInfoPrinter = $phpDocInfoPrinter;
}
/**
@ -127,10 +138,6 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
$this->updateAttributes($node);
$this->keepFileInfoAttribute($node, $originalNode);
$this->notifyNodeChangeFileInfo($node);
// doc block has changed
} elseif ($node->getComments() !== $originalComment || $node->getDocComment() !== $originalDocComment) {
$this->notifyNodeChangeFileInfo($node);
}
// if stmt ("$value;") was replaced by expr ("$value"), add the ending ";" (Expression) to prevent breaking the code
@ -275,6 +282,10 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
private function hasNodeChanged(Node $originalNode, Node $node): bool
{
if ($this->isNameIdentical($node, $originalNode)) {
return false;
}
return ! $this->areNodesEqual($originalNode, $node);
}
@ -314,4 +325,18 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
$this->addNodeAfterNode($ifStmt, $node);
}
}
private function isNameIdentical(Node $node, Node $originalNode): bool
{
if (static::class !== ImportFullyQualifiedNamesRector::class) {
return false;
}
if (! $originalNode instanceof Name) {
return false;
}
// names are the same
return $this->areNodesEqual($originalNode->getAttribute('originalName'), $node);
}
}

View File

@ -63,6 +63,15 @@ trait BetterStandardPrinterTrait
return $this->betterStandardPrinter->areNodesEqual($firstNode, $secondNode);
}
/**
* @param Node|Node[]|null $firstNode
* @param Node|Node[]|null $secondNode
*/
protected function areNodesWithoutCommentsEqual($firstNode, $secondNode): bool
{
return $this->betterStandardPrinter->areNodesWithoutCommentsEqual($firstNode, $secondNode);
}
/**
* @param Node[] $availableNodes
*/

View File

@ -6,6 +6,7 @@ namespace Rector\Rector\AbstractRector;
use PhpParser\Node;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\NodeTypeResolver\StaticTypeMapper;
@ -38,10 +39,18 @@ trait DocBlockManipulatorTrait
protected function getPhpDocInfo(Node $node): ?PhpDocInfo
{
if ($node->getAttribute(AttributeKey::PHP_DOC_INFO)) {
return $node->getAttribute(AttributeKey::PHP_DOC_INFO);
}
// @todo always use PhpDocInfo even for empty nodes, for consisteny object API; same way Nop node does
if ($node->getDocComment() === null) {
return null;
}
return $this->docBlockManipulator->createPhpDocInfoFromNode($node);
$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($node);
$node->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo);
return $phpDocInfo;
}
}

View File

@ -101,6 +101,8 @@ trait NodeFactoryTrait
*/
protected function createFunction(string $name, array $arguments = []): FuncCall
{
$arguments = $this->createArgs($arguments);
return new FuncCall(new Name($name), $arguments);
}

View File

@ -6,6 +6,7 @@ namespace Rector\Rector\Property;
use DI\Annotation\Inject as PHPDIInject;
use JMS\DiExtraBundle\Annotation\Inject as JMSInject;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
@ -139,7 +140,12 @@ PHP
continue;
}
if ($this->isParameterInject($injectTagValueNode)) {
return null;
}
$type = $this->resolveType($node, $injectTagValueNode);
return $this->refactorPropertyWithAnnotation($node, $type, $tagClass);
}
@ -233,4 +239,18 @@ PHP
return new MixedType();
}
private function isParameterInject(PhpDocTagValueNode $phpDocTagValueNode): bool
{
if (! $phpDocTagValueNode instanceof JMSInjectTagValueNode) {
return false;
}
$serviceName = $phpDocTagValueNode->getServiceName();
if ($serviceName === null) {
return false;
}
return (bool) Strings::match($serviceName, '#%(.*?)%#');
}
}

View File

@ -15,11 +15,6 @@ class SomeController
* @DI\Inject("translator", strict = false, required = false)
*/
private $translator;
/**
* @DI\Inject("%kernel.cache_dir%")
*/
private $cacheDir;
}
?>
@ -36,16 +31,10 @@ class SomeController
* @var \Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeEntityManager
*/
private $entityManager;
/**
* @var \Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeTranslatorInterface
*/
private $translator;
/**
* @DI\Inject("%kernel.cache_dir%")
*/
private $cacheDir;
public function __construct(\Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeEntityManager $entityManager, \Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeTranslatorInterface $translator)
{
$this->entityManager = $entityManager;

View File

@ -0,0 +1,13 @@
<?php
namespace Rector\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
use JMS\DiExtraBundle\Annotation as DI;
class SkipParameterInject
{
/**
* @DI\Inject("%kernel.cache_dir%")
*/
private $cacheDir;
}