From c5a315e2c4c5bd979f171cfb0c7d241248d2488b Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 25 Nov 2024 14:22:30 +0000 Subject: [PATCH] Updated Rector to commit 12b471059bc5717e3f87cf1e811f05396e3571cf https://github.com/rectorphp/rector-src/commit/12b471059bc5717e3f87cf1e811f05396e3571cf [DeadCode] Skip private dataProvider method on RemoveUnusedPrivateMethodRector (#6509) --- .../RemoveUnusedPrivateMethodRector.php | 64 ++++++++++++++++++- src/Application/VersionResolver.php | 4 +- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php index 9467aed3198..9386adbd2de 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php @@ -8,9 +8,13 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; +use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\Reflection\ClassReflection; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\DeadCode\NodeAnalyzer\IsClassMethodUsedAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; use Rector\PhpParser\Node\BetterNodeFinder; use Rector\PHPStan\ScopeFetcher; use Rector\Rector\AbstractRector; @@ -35,11 +39,21 @@ final class RemoveUnusedPrivateMethodRector extends AbstractRector * @readonly */ private BetterNodeFinder $betterNodeFinder; - public function __construct(IsClassMethodUsedAnalyzer $isClassMethodUsedAnalyzer, ReflectionResolver $reflectionResolver, BetterNodeFinder $betterNodeFinder) + /** + * @readonly + */ + private PhpDocInfoFactory $phpDocInfoFactory; + /** + * @readonly + */ + private PhpAttributeAnalyzer $phpAttributeAnalyzer; + public function __construct(IsClassMethodUsedAnalyzer $isClassMethodUsedAnalyzer, ReflectionResolver $reflectionResolver, BetterNodeFinder $betterNodeFinder, PhpDocInfoFactory $phpDocInfoFactory, PhpAttributeAnalyzer $phpAttributeAnalyzer) { $this->isClassMethodUsedAnalyzer = $isClassMethodUsedAnalyzer; $this->reflectionResolver = $reflectionResolver; $this->betterNodeFinder = $betterNodeFinder; + $this->phpDocInfoFactory = $phpDocInfoFactory; + $this->phpAttributeAnalyzer = $phpAttributeAnalyzer; } public function getRuleDefinition() : RuleDefinition { @@ -95,6 +109,7 @@ CODE_SAMPLE } $hasChanged = \false; $classReflection = $this->reflectionResolver->resolveClassReflection($node); + $collectionTestMethodsUsesPrivateProvider = $this->collectTestMethodsUsesPrivateDataProvider($classReflection, $node, $classMethods); foreach ($privateMethods as $privateMethod) { if ($this->shouldSkip($privateMethod, $classReflection)) { continue; @@ -102,6 +117,9 @@ CODE_SAMPLE if ($this->isClassMethodUsedAnalyzer->isClassMethodUsed($node, $privateMethod, $scope)) { continue; } + if (\in_array($this->getName($privateMethod), $collectionTestMethodsUsesPrivateProvider, \true)) { + continue; + } unset($node->stmts[$privateMethod->getAttribute(AttributeKey::STMT_KEY)]); $hasChanged = \true; } @@ -110,6 +128,50 @@ CODE_SAMPLE } return null; } + /** + * @param ClassMethod[] $classMethods + * @return string[] + */ + private function collectTestMethodsUsesPrivateDataProvider(ClassReflection $classReflection, Class_ $class, array $classMethods) : array + { + if (!$classReflection->isSubClassOf('PHPUnit\\Framework\\TestCase')) { + return []; + } + $privateMethods = []; + foreach ($classMethods as $classMethod) { + // test method only public, but may use private data provider + // so verify @dataProvider and #[\PHPUnit\Framework\Attributes\DataProvider] only on public methods + if (!$classMethod->isPublic()) { + continue; + } + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + if ($phpDocInfo->hasByName('dataProvider')) { + $dataProvider = $phpDocInfo->getByName('dataProvider'); + if ($dataProvider instanceof PhpDocTagNode && $dataProvider->value instanceof GenericTagValueNode) { + $dataProviderMethod = $class->getMethod($dataProvider->value->value); + if ($dataProviderMethod instanceof ClassMethod && $dataProviderMethod->isPrivate()) { + $privateMethods[] = $dataProvider->value->value; + } + } + } + if ($this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, 'PHPUnit\\Framework\\Attributes\\DataProvider')) { + foreach ($classMethod->attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attr) { + if ($attr->name->toString() === 'PHPUnit\\Framework\\Attributes\\DataProvider') { + $argValue = $attr->args[0]->value->value ?? ''; + if (\is_string($argValue)) { + $dataProviderMethod = $class->getMethod($argValue); + if ($dataProviderMethod instanceof ClassMethod && $dataProviderMethod->isPrivate()) { + $privateMethods[] = $argValue; + } + } + } + } + } + } + } + return $privateMethods; + } private function shouldSkip(ClassMethod $classMethod, ?ClassReflection $classReflection) : bool { if (!$classReflection instanceof ClassReflection) { diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index 25cd6fd741c..4ccee918ceb 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -19,12 +19,12 @@ final class VersionResolver * @api * @var string */ - public const PACKAGE_VERSION = '1ec42bcc33f0607533e08d75c47b589d89e7649e'; + public const PACKAGE_VERSION = '12b471059bc5717e3f87cf1e811f05396e3571cf'; /** * @api * @var string */ - public const RELEASE_DATE = '2024-11-24 18:23:37'; + public const RELEASE_DATE = '2024-11-25 21:20:15'; /** * @var int */