mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-27 05:03:31 +00:00
[DeadCode] Make private property removal dependent on local property fetches (#311)
This commit is contained in:
parent
46843ec495
commit
e3727aad81
|
@ -11,7 +11,6 @@ use Rector\Privatization\Rector\ClassMethod\PrivatizeFinalClassMethodRector;
|
|||
use Rector\Privatization\Rector\MethodCall\PrivatizeLocalGetterToPropertyRector;
|
||||
use Rector\Privatization\Rector\Property\ChangeReadOnlyPropertyWithDefaultValueToConstantRector;
|
||||
use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector;
|
||||
use Rector\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
|
@ -22,7 +21,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
$services->set(ChangeReadOnlyVariableWithDefaultValueToConstantRector::class);
|
||||
$services->set(RepeatedLiteralToClassConstantRector::class);
|
||||
$services->set(PrivatizeLocalGetterToPropertyRector::class);
|
||||
$services->set(PrivatizeLocalPropertyToPrivatePropertyRector::class);
|
||||
$services->set(PrivatizeFinalClassPropertyRector::class);
|
||||
$services->set(PrivatizeFinalClassMethodRector::class);
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ use Rector\NodeTypeResolver\NodeTypeResolver;
|
|||
final class NodeRepository
|
||||
{
|
||||
public function __construct(
|
||||
private ParsedPropertyFetchNodeCollector $parsedPropertyFetchNodeCollector,
|
||||
private NodeNameResolver $nodeNameResolver,
|
||||
private ParsedNodeCollector $parsedNodeCollector,
|
||||
private ReflectionProvider $reflectionProvider,
|
||||
|
@ -33,39 +32,6 @@ final class NodeRepository
|
|||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PropertyFetch[]
|
||||
*/
|
||||
public function findPropertyFetchesByProperty(Property $property): array
|
||||
{
|
||||
/** @var string|null $className */
|
||||
$className = $property->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
return $this->parsedPropertyFetchNodeCollector->findPropertyFetchesByTypeAndName($className, $propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PropertyFetch[]
|
||||
*/
|
||||
public function findPropertyFetchesByPropertyFetch(PropertyFetch $propertyFetch): array
|
||||
{
|
||||
$propertyFetcheeType = $this->nodeTypeResolver->getStaticType($propertyFetch->var);
|
||||
if (! $propertyFetcheeType instanceof TypeWithClassName) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$className = $this->nodeTypeResolver->getFullyQualifiedClassName($propertyFetcheeType);
|
||||
|
||||
/** @var string $propertyName */
|
||||
$propertyName = $this->nodeNameResolver->getName($propertyFetch);
|
||||
|
||||
return $this->parsedPropertyFetchNodeCollector->findPropertyFetchesByTypeAndName($className, $propertyName);
|
||||
}
|
||||
|
||||
public function hasClassChildren(Class_ $desiredClass): bool
|
||||
{
|
||||
$desiredClassName = $desiredClass->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
@ -90,6 +56,7 @@ final class NodeRepository
|
|||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use ReflectionProvider instead to resolve all the traits
|
||||
* @return Trait_[]
|
||||
*/
|
||||
public function findUsedTraitsInClass(ClassLike $classLike): array
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeCollector\NodeCollector;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Expr\StaticPropertyFetch;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
/**
|
||||
* All parsed nodes grouped type
|
||||
*/
|
||||
final class ParsedPropertyFetchNodeCollector
|
||||
{
|
||||
/**
|
||||
* @var array<string, array<string, PropertyFetch[]>>
|
||||
*/
|
||||
private array $propertyFetchesByTypeAndName = [];
|
||||
|
||||
public function __construct(
|
||||
private NodeNameResolver $nodeNameResolver,
|
||||
private NodeTypeResolver $nodeTypeResolver
|
||||
) {
|
||||
}
|
||||
|
||||
public function collect(Node $node): void
|
||||
{
|
||||
if (! $node instanceof PropertyFetch && ! $node instanceof StaticPropertyFetch) {
|
||||
return;
|
||||
}
|
||||
|
||||
$propertyType = $this->resolvePropertyCallerType($node);
|
||||
if ($propertyType instanceof MixedType) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure name is valid
|
||||
if ($node->name instanceof StaticCall || $node->name instanceof MethodCall) {
|
||||
return;
|
||||
}
|
||||
|
||||
$propertyName = $this->nodeNameResolver->getName($node->name);
|
||||
if ($propertyName === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addPropertyFetchWithTypeAndName($propertyType, $node, $propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PropertyFetch[]
|
||||
*/
|
||||
public function findPropertyFetchesByTypeAndName(string $className, string $propertyName): array
|
||||
{
|
||||
return $this->propertyFetchesByTypeAndName[$className][$propertyName] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PropertyFetch|StaticPropertyFetch $node
|
||||
*/
|
||||
private function resolvePropertyCallerType(Node $node): Type
|
||||
{
|
||||
if ($node instanceof PropertyFetch) {
|
||||
return $this->nodeTypeResolver->resolve($node->var);
|
||||
}
|
||||
|
||||
return $this->nodeTypeResolver->resolve($node->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PropertyFetch|StaticPropertyFetch $propertyFetchNode
|
||||
*/
|
||||
private function addPropertyFetchWithTypeAndName(
|
||||
Type $propertyType,
|
||||
Node $propertyFetchNode,
|
||||
string $propertyName
|
||||
): void {
|
||||
if ($propertyType instanceof TypeWithClassName) {
|
||||
$this->propertyFetchesByTypeAndName[$propertyType->getClassName()][$propertyName][] = $propertyFetchNode;
|
||||
}
|
||||
|
||||
if ($propertyType instanceof UnionType) {
|
||||
foreach ($propertyType->getTypes() as $unionedType) {
|
||||
$this->addPropertyFetchWithTypeAndName($unionedType, $propertyFetchNode, $propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,13 +7,11 @@ namespace Rector\NodeCollector\NodeVisitor;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use Rector\NodeCollector\NodeCollector\ParsedNodeCollector;
|
||||
use Rector\NodeCollector\NodeCollector\ParsedPropertyFetchNodeCollector;
|
||||
|
||||
final class NodeCollectorNodeVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
public function __construct(
|
||||
private ParsedNodeCollector $parsedNodeCollector,
|
||||
private ParsedPropertyFetchNodeCollector $parsedPropertyFetchNodeCollector
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -23,8 +21,6 @@ final class NodeCollectorNodeVisitor extends NodeVisitorAbstract
|
|||
$this->parsedNodeCollector->collect($node);
|
||||
}
|
||||
|
||||
$this->parsedPropertyFetchNodeCollector->collect($node);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,13 +32,8 @@ final class ReadWritePropertyAnalyzer
|
|||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PropertyFetch|StaticPropertyFetch $node
|
||||
*/
|
||||
public function isRead(Node $node): bool
|
||||
public function isRead(PropertyFetch|StaticPropertyFetch $node): bool
|
||||
{
|
||||
Assert::isAnyOf($node, [PropertyFetch::class, StaticPropertyFetch::class]);
|
||||
|
||||
$parent = $node->getAttribute(AttributeKey::PARENT_NODE);
|
||||
if (! $parent instanceof Node) {
|
||||
throw new ShouldNotHappenException();
|
||||
|
|
|
@ -6,12 +6,10 @@ namespace Rector\ReadWrite\NodeFinder;
|
|||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\Foreach_;
|
||||
use Rector\Core\PhpParser\Comparing\NodeComparator;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeNestingScope\NodeFinder\ScopeAwareNodeFinder;
|
||||
|
||||
|
@ -20,7 +18,6 @@ final class NodeUsageFinder
|
|||
public function __construct(
|
||||
private NodeNameResolver $nodeNameResolver,
|
||||
private BetterNodeFinder $betterNodeFinder,
|
||||
private NodeRepository $nodeRepository,
|
||||
private ScopeAwareNodeFinder $scopeAwareNodeFinder,
|
||||
private NodeComparator $nodeComparator
|
||||
) {
|
||||
|
@ -50,25 +47,6 @@ final class NodeUsageFinder
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PropertyFetch[]
|
||||
*/
|
||||
public function findPropertyFetchUsages(PropertyFetch $desiredPropertyFetch): array
|
||||
{
|
||||
$propertyFetches = $this->nodeRepository->findPropertyFetchesByPropertyFetch($desiredPropertyFetch);
|
||||
|
||||
$propertyFetchesWithoutPropertyFetch = [];
|
||||
foreach ($propertyFetches as $propertyFetch) {
|
||||
if ($propertyFetch === $desiredPropertyFetch) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$propertyFetchesWithoutPropertyFetch[] = $propertyFetch;
|
||||
}
|
||||
|
||||
return $propertyFetchesWithoutPropertyFetch;
|
||||
}
|
||||
|
||||
public function findPreviousForeachNodeUsage(Foreach_ $foreach, Expr $expr): ?Node
|
||||
{
|
||||
return $this->scopeAwareNodeFinder->findParent($foreach, function (Node $node) use ($expr): bool {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\ReadWrite\ReadNodeAnalyzer;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\ReadWrite\Contract\ReadNodeAnalyzerInterface;
|
||||
|
||||
final class LocalPropertyFetchReadNodeAnalyzer implements ReadNodeAnalyzerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private JustReadExprAnalyzer $justReadExprAnalyzer,
|
||||
private PropertyFetchFinder $propertyFetchFinder,
|
||||
private NodeNameResolver $nodeNameResolver
|
||||
) {
|
||||
}
|
||||
|
||||
public function supports(Node $node): bool
|
||||
{
|
||||
return $node instanceof PropertyFetch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PropertyFetch $node
|
||||
*/
|
||||
public function isRead(Node $node): bool
|
||||
{
|
||||
$class = $node->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $class instanceof Class_) {
|
||||
// assume worse to keep node protected
|
||||
return true;
|
||||
}
|
||||
|
||||
$propertyName = $this->nodeNameResolver->getName($node->name);
|
||||
if ($propertyName === null) {
|
||||
// assume worse to keep node protected
|
||||
return true;
|
||||
}
|
||||
|
||||
$propertyFetches = $this->propertyFetchFinder->findLocalPropertyFetchesByName($class, $propertyName);
|
||||
foreach ($propertyFetches as $propertyFetch) {
|
||||
if ($this->justReadExprAnalyzer->isReadContext($propertyFetch)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\ReadWrite\ReadNodeAnalyzer;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use Rector\ReadWrite\Contract\ReadNodeAnalyzerInterface;
|
||||
use Rector\ReadWrite\NodeFinder\NodeUsageFinder;
|
||||
|
||||
final class PropertyFetchReadNodeAnalyzer implements ReadNodeAnalyzerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private JustReadExprAnalyzer $justReadExprAnalyzer,
|
||||
private NodeUsageFinder $nodeUsageFinder
|
||||
) {
|
||||
}
|
||||
|
||||
public function supports(Node $node): bool
|
||||
{
|
||||
return $node instanceof PropertyFetch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PropertyFetch $node
|
||||
*/
|
||||
public function isRead(Node $node): bool
|
||||
{
|
||||
$propertyFetchUsages = $this->nodeUsageFinder->findPropertyFetchUsages($node);
|
||||
foreach ($propertyFetchUsages as $propertyFetchUsage) {
|
||||
if ($this->justReadExprAnalyzer->isReadContext($propertyFetchUsage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
|
|||
use Rector\Php74\Rector\MethodCall\ChangeReflectionTypeToStringToGetNameRector;
|
||||
use Rector\PHPUnit\Rector\Class_\AddSeeTestAnnotationRector;
|
||||
use Rector\PHPUnit\Set\PHPUnitSetList;
|
||||
use Rector\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector;
|
||||
use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
|
||||
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
|
@ -88,8 +87,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
__DIR__ . '/rules/Php70/Rector/FuncCall/MultiDirnameRector.php',
|
||||
],
|
||||
|
||||
PrivatizeLocalPropertyToPrivatePropertyRector::class => [__DIR__ . '/src/Rector/AbstractRector.php'],
|
||||
|
||||
ReturnTypeDeclarationRector::class => [
|
||||
__DIR__ . '/packages/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php',
|
||||
__DIR__ . '/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php',
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
class Fixture
|
||||
{
|
||||
public $value;
|
||||
|
||||
public function run()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
class Fixture
|
||||
{
|
||||
private $value;
|
||||
|
||||
public function run()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
abstract class SkipAbstract
|
||||
{
|
||||
public $property;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
abstract class SkipAbstractClass
|
||||
{
|
||||
public $some;
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
use PhpParser\NodeTraverser;
|
||||
|
||||
class SkipAnonymousClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$anonymousClass = new class() extends NodeTraverser
|
||||
{
|
||||
public $property;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
class SkipClassExtended extends AbstractParentClass
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->value;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractParentClass
|
||||
{
|
||||
public $value;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
use Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Source\EventInterface;
|
||||
|
||||
final class SkipDimFetchDispatcher
|
||||
{
|
||||
/**
|
||||
* @var EventInterface[]
|
||||
*/
|
||||
private $dispatched = [];
|
||||
|
||||
public function dispatch(EventInterface $event)
|
||||
{
|
||||
$this->dispatched[] = $event;
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EventInterface[]
|
||||
*/
|
||||
public function getDispatchedEvents(): array
|
||||
{
|
||||
return $this->dispatched;
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
class SkipExternalFetch
|
||||
{
|
||||
/**
|
||||
* @var ExternalClass
|
||||
*/
|
||||
private $externalClass;
|
||||
|
||||
public function __construct(ExternalClass $externalClass)
|
||||
{
|
||||
$this->externalClass = $externalClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->externalClass->externalProperty;
|
||||
}
|
||||
}
|
||||
|
||||
class ExternalClass
|
||||
{
|
||||
public $externalProperty;
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
class SkipInterfaceImplemented
|
||||
{
|
||||
/**
|
||||
* @var SomeInterface
|
||||
*/
|
||||
private $classImplementing;
|
||||
|
||||
public function __construct(SomeInterface $classImplementing)
|
||||
{
|
||||
$this->classImplementing = $classImplementing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->classImplementing->run();
|
||||
}
|
||||
}
|
||||
|
||||
class ClassImplementingInterface implements SomeInterface
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface SomeInterface
|
||||
{
|
||||
public function run();
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class SkipParentClassProtected extends Command
|
||||
{
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
use PHP_CodeSniffer\Files\File;
|
||||
use PHP_CodeSniffer\Sniffs\Sniff;
|
||||
|
||||
final class SkipSniff implements Sniff
|
||||
{
|
||||
public $sniffConfigProperty;
|
||||
|
||||
public function shouldSkip(string $methodName, string $className): bool
|
||||
{
|
||||
// not really a setter, but usually test "setup" method
|
||||
if ($methodName === 'setUp') {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->sniffConfigProperty as $allowedClass) {
|
||||
if (fnmatch($allowedClass, $className, FNM_NOESCAPE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
// TODO: Implement register() method.
|
||||
}
|
||||
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
// TODO: Implement process() method.
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
trait SkipTrait
|
||||
{
|
||||
public $value;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Fixture;
|
||||
|
||||
final class SkipUsedInTrait
|
||||
{
|
||||
public $go;
|
||||
}
|
||||
|
||||
trait SomeTrait
|
||||
{
|
||||
/**
|
||||
* @var SkipUsedInTrait
|
||||
*/
|
||||
private $skipUsedInTrait;
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->skipUsedInTrait->go;
|
||||
}
|
||||
}
|
||||
|
||||
class AnotherClassUsingTheTraitJustToMakePHPStanWork
|
||||
{
|
||||
use SomeTrait;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class PrivatizeLocalPropertyToPrivatePropertyRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
public function provideConfigFilePath(): string
|
||||
{
|
||||
return __DIR__ . '/config/configured_rule.php';
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\Source;
|
||||
|
||||
interface EventInterface
|
||||
{
|
||||
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(PrivatizeLocalPropertyToPrivatePropertyRector::class);
|
||||
};
|
|
@ -10,6 +10,7 @@ use PhpParser\Node\Stmt\Interface_;
|
|||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use Rector\Core\NodeManipulator\PropertyManipulator;
|
||||
use Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\Removing\NodeManipulator\ComplexNodeRemover;
|
||||
|
@ -23,7 +24,7 @@ final class RemoveUnusedPrivatePropertyRector extends AbstractRector
|
|||
{
|
||||
public function __construct(
|
||||
private PropertyManipulator $propertyManipulator,
|
||||
private ComplexNodeRemover $complexNodeRemover
|
||||
private ComplexNodeRemover $complexNodeRemover,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Privatization\Rector\Property;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\VendorLocker\NodeVendorLocker\PropertyVisibilityVendorLockResolver;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector\PrivatizeLocalPropertyToPrivatePropertyRectorTest
|
||||
*/
|
||||
final class PrivatizeLocalPropertyToPrivatePropertyRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private const TAGS_REQUIRING_PUBLIC_PROPERTY = [
|
||||
'api',
|
||||
// Symfony DI
|
||||
'required',
|
||||
// Nette + other DI
|
||||
'inject',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var ObjectType[]
|
||||
*/
|
||||
private array $excludedObjectTypes = [];
|
||||
|
||||
public function __construct(
|
||||
private PropertyVisibilityVendorLockResolver $propertyVisibilityVendorLockResolver,
|
||||
private ClassAnalyzer $classAnalyzer
|
||||
) {
|
||||
$this->excludedObjectTypes = [
|
||||
new ObjectType('PHPUnit\Framework\TestCase'),
|
||||
new ObjectType('PHP_CodeSniffer\Sniffs\Sniff'),
|
||||
];
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Privatize local-only property to private property', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public $value;
|
||||
|
||||
public function run()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
private $value;
|
||||
|
||||
public function run()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Property::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Property $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if ($this->shouldSkip($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$propertyFetches = $this->nodeRepository->findPropertyFetchesByProperty($node);
|
||||
|
||||
$usedPropertyFetchClassNames = [];
|
||||
foreach ($propertyFetches as $propertyFetch) {
|
||||
$usedPropertyFetchClassNames[] = $propertyFetch->getAttribute(AttributeKey::CLASS_NAME);
|
||||
}
|
||||
|
||||
$usedPropertyFetchClassNames = array_unique($usedPropertyFetchClassNames);
|
||||
|
||||
$propertyClassName = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
// has external usage
|
||||
if ($usedPropertyFetchClassNames !== [] && [$propertyClassName] !== $usedPropertyFetchClassNames) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->visibilityManipulator->makePrivate($node);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function shouldSkip(Property $property): bool
|
||||
{
|
||||
$classLike = $property->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $classLike instanceof ClassLike) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->shouldSkipClass($classLike)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->shouldSkipProperty($property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// is parent required property? skip it
|
||||
if ($this->propertyVisibilityVendorLockResolver->isParentLockedProperty($property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->propertyVisibilityVendorLockResolver->isChildLockedProperty($property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
|
||||
return $phpDocInfo->hasByNames(self::TAGS_REQUIRING_PUBLIC_PROPERTY);
|
||||
}
|
||||
|
||||
private function shouldSkipClass(ClassLike $classLike): bool
|
||||
{
|
||||
if (! $classLike instanceof Class_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->classAnalyzer->isAnonymousClass($classLike)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->nodeTypeResolver->isObjectTypes($classLike, $this->excludedObjectTypes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $classLike->isAbstract();
|
||||
}
|
||||
|
||||
private function shouldSkipProperty(Property $property): bool
|
||||
{
|
||||
// already private
|
||||
if ($property->isPrivate()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// skip for now
|
||||
return $property->isStatic();
|
||||
}
|
||||
}
|
|
@ -29,15 +29,10 @@ final class IdentifierManipulator
|
|||
}
|
||||
|
||||
/**
|
||||
* @param ClassConstFetch|MethodCall|PropertyFetch|StaticCall|ClassMethod $node
|
||||
* @param string[] $renameMethodMap
|
||||
*/
|
||||
public function renameNodeWithMap(Node $node, array $renameMethodMap): void
|
||||
public function renameNodeWithMap(ClassConstFetch|MethodCall|PropertyFetch|StaticCall|ClassMethod $node, array $renameMethodMap): void
|
||||
{
|
||||
Assert::isAnyOf($node, [
|
||||
ClassConstFetch::class, MethodCall::class, PropertyFetch::class, StaticCall::class, ClassMethod::class,
|
||||
]);
|
||||
|
||||
$oldNodeMethodName = $this->resolveOldMethodName($node);
|
||||
if ($oldNodeMethodName === null) {
|
||||
return;
|
||||
|
@ -46,23 +41,14 @@ final class IdentifierManipulator
|
|||
$node->name = new Identifier($renameMethodMap[$oldNodeMethodName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassConstFetch|MethodCall|PropertyFetch|StaticCall|ClassMethod $node
|
||||
*/
|
||||
public function removeSuffix(Node $node, string $suffixToRemove): void
|
||||
public function removeSuffix(ClassConstFetch|MethodCall|PropertyFetch|StaticCall|ClassMethod $node, string $suffixToRemove): void
|
||||
{
|
||||
Assert::isAnyOf(
|
||||
$node,
|
||||
[ClassConstFetch::class, MethodCall::class, PropertyFetch::class, StaticCall::class, ClassMethod::class]
|
||||
);
|
||||
|
||||
$name = $this->nodeNameResolver->getName($node);
|
||||
if ($name === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$newName = Strings::replace($name, sprintf('#%s$#', $suffixToRemove), '');
|
||||
|
||||
$node->name = new Identifier($newName);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user