Skip to content

Commit

Permalink
[PHP 8.0] Add method param to ConstantListClassToEnumRector (#2415)
Browse files Browse the repository at this point in the history
* add skipped fixtures

* skip non-scalar types

* prepare parameter update

* add ConstExprClassNameDecorator, add PhpDocNodeDecoratorInterface
  • Loading branch information
TomasVotruba committed Jun 2, 2022
1 parent ba0869a commit a73dafd
Show file tree
Hide file tree
Showing 17 changed files with 406 additions and 34 deletions.
2 changes: 2 additions & 0 deletions easy-ci.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare(strict_types=1);

use PHPStan\PhpDocParser\Parser\TypeParser;
use Rector\BetterPhpDocParser\Contract\PhpDocParser\PhpDocNodeDecoratorInterface;
use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface;
use Rector\Core\Contract\Console\OutputStyleInterface;
use Rector\Core\Contract\PhpParser\Node\StmtsAwareInterface;
Expand Down Expand Up @@ -40,6 +41,7 @@
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::TYPES_TO_SKIP, [
PhpDocNodeDecoratorInterface::class,
Command::class,
Application::class,
RectorInterface::class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Rector\BetterPhpDocParser\Contract\PhpDocParser;

use PhpParser\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;

interface PhpDocNodeDecoratorInterface
{
public function decorate(PhpDocNode $phpDocNode, Node $phpNode): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function __construct(
private readonly StaticTypeMapper $staticTypeMapper,
private readonly AnnotationNaming $annotationNaming,
private readonly RectorChangeCollector $rectorChangeCollector,
private readonly PhpDocNodeByTypeFinder $phpDocNodeByTypeFinder
private readonly PhpDocNodeByTypeFinder $phpDocNodeByTypeFinder,
) {
}

Expand Down
27 changes: 20 additions & 7 deletions packages/BetterPhpDocParser/PhpDocParser/BetterPhpDocParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Rector\BetterPhpDocParser\PhpDocParser;

use PhpParser\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
Expand All @@ -14,10 +15,12 @@
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use Rector\BetterPhpDocParser\Contract\PhpDocParser\PhpDocNodeDecoratorInterface;
use Rector\BetterPhpDocParser\PhpDocInfo\TokenIteratorFactory;
use Rector\BetterPhpDocParser\ValueObject\Parser\BetterTokenIterator;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\BetterPhpDocParser\ValueObject\StartAndEnd;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Exception\ShouldNotHappenException;
use Symplify\PackageBuilder\Reflection\PrivatesCaller;

Expand All @@ -26,17 +29,18 @@
*/
final class BetterPhpDocParser extends PhpDocParser
{
private readonly PrivatesCaller $privatesCaller;

/**
* @param PhpDocNodeDecoratorInterface[] $phpDocNodeDecorators
*/
public function __construct(
TypeParser $typeParser,
ConstExprParser $constExprParser,
private readonly CurrentNodeProvider $currentNodeProvider,
private readonly TokenIteratorFactory $tokenIteratorFactory,
private readonly DoctrineAnnotationDecorator $doctrineAnnotationDecorator
private readonly array $phpDocNodeDecorators,
private readonly PrivatesCaller $privatesCaller = new PrivatesCaller(),
) {
parent::__construct($typeParser, $constExprParser);

$this->privatesCaller = new PrivatesCaller();
}

public function parse(TokenIterator $tokenIterator): PhpDocNode
Expand All @@ -59,14 +63,23 @@ public function parse(TokenIterator $tokenIterator): PhpDocNode
$tokenIterator->tryConsumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC);

$phpDocNode = new PhpDocNode($children);
// replace generic nodes with DoctrineAnnotations
$this->doctrineAnnotationDecorator->decorate($phpDocNode);

// decorate FQN classes etc.
$node = $this->currentNodeProvider->getNode();
if (! $node instanceof Node) {
throw new ShouldNotHappenException();
}

foreach ($this->phpDocNodeDecorators as $phpDocNodeDecorator) {
$phpDocNodeDecorator->decorate($phpDocNode, $node);
}

return $phpDocNode;
}

public function parseTag(TokenIterator $tokenIterator): PhpDocTagNode
{
// replace generic nodes with DoctrineAnnotations
if (! $tokenIterator instanceof BetterTokenIterator) {
throw new ShouldNotHappenException();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace Rector\BetterPhpDocParser\PhpDocParser;

use PhpParser\Node as PhpNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use Rector\BetterPhpDocParser\Contract\PhpDocParser\PhpDocNodeDecoratorInterface;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\StaticTypeMapper\Naming\NameScopeFactory;
use Symplify\Astral\PhpDocParser\PhpDocNodeTraverser;

/**
* Decorate node with fully qualified class name for const epxr,
* e.g. Direction::*
*/
final class ConstExprClassNameDecorator implements PhpDocNodeDecoratorInterface
{
public function __construct(
private readonly NameScopeFactory $nameScopeFactory,
private readonly PhpDocNodeTraverser $phpDocNodeTraverser
) {
}

public function decorate(PhpDocNode $phpDocNode, PhpNode $phpNode): void
{
$this->phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', function (Node $node) use (
$phpNode
): Node|null {
if (! $node instanceof ConstExprNode) {
return null;
}

$className = $this->resolveFullyQualifiedClass($node, $phpNode);
if ($className === null) {
return null;
}

$node->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $className);
return $node;
});
}

private function resolveFullyQualifiedClass(ConstExprNode $constExprNode, PhpNode $phpNode): ?string
{
if (! $constExprNode instanceof ConstFetchNode) {
return null;
}

$nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($phpNode);
return $nameScope->resolveStringName($constExprNode->className);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,16 @@
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use Rector\BetterPhpDocParser\Attributes\AttributeMirrorer;
use Rector\BetterPhpDocParser\Contract\PhpDocParser\PhpDocNodeDecoratorInterface;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDoc\SpacelessPhpDocTagNode;
use Rector\BetterPhpDocParser\PhpDocInfo\TokenIteratorFactory;
use Rector\BetterPhpDocParser\ValueObject\DoctrineAnnotation\SilentKeyMap;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\BetterPhpDocParser\ValueObject\StartAndEnd;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Util\StringUtils;

final class DoctrineAnnotationDecorator
final class DoctrineAnnotationDecorator implements PhpDocNodeDecoratorInterface
{
/**
* Special short annotations, that are resolved as FQN by Doctrine annotation parser
Expand All @@ -45,24 +44,18 @@ final class DoctrineAnnotationDecorator
private const NESTED_ANNOTATION_END_REGEX = '#(\s+)?\}\)(\s+)?#';

public function __construct(
private readonly CurrentNodeProvider $currentNodeProvider,
private readonly ClassAnnotationMatcher $classAnnotationMatcher,
private readonly StaticDoctrineAnnotationParser $staticDoctrineAnnotationParser,
private readonly TokenIteratorFactory $tokenIteratorFactory,
private readonly AttributeMirrorer $attributeMirrorer
) {
}

public function decorate(PhpDocNode $phpDocNode): void
public function decorate(PhpDocNode $phpDocNode, Node $phpNode): void
{
$currentPhpNode = $this->currentNodeProvider->getNode();
if (! $currentPhpNode instanceof Node) {
throw new ShouldNotHappenException();
}

// merge split doctrine nested tags
$this->mergeNestedDoctrineAnnotations($phpDocNode);
$this->transformGenericTagValueNodesToDoctrineAnnotationTagValueNodes($phpDocNode, $currentPhpNode);
$this->transformGenericTagValueNodesToDoctrineAnnotationTagValueNodes($phpDocNode, $phpNode);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;

final class ChangeParamType
{
/**
* @param Gear::* $gear
*/
public function changeGear(string $gear)
{
}
}

?>
-----
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;

final class ChangeParamType
{
public function changeGear(\Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear $gear)
{
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;

final class MultipleParamsChange
{
/**
* @param string $carType
* @param Gear::* $gear
* @param int $speed
*/
public function changeGear($carType, string $gear, $speed)
{
}
}

?>
-----
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;

final class MultipleParamsChange
{
/**
* @param string $carType
* @param int $speed
*/
public function changeGear($carType, \Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear $gear, $speed)
{
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

final class SkipAlsoOtherElements
{
public const LEFT = 'left';

public const RIGHT = 'right';

protected $value;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

final class SkipDifferentType
{
public const LEFT = 'left';

public const RIGHT = 5;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

final class SkipNonPublicConst
{
public const LEFT = 'left';

protected const RIGHT = 5;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

final class SkipNonScalarTypes
{
public const LEFT = self::class;

public const RIGHT = self::class;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

final class SkipUnknownClass
{
/**
* @param AnythingNonExisting::* $gear
*/
public function changeGear(string $gear)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source;

final class Gear
{
public const FIRST = 'first';

public const SECOND = 'second';
}

0 comments on commit a73dafd

Please sign in to comment.