diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_expression.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_expression.php.inc new file mode 100644 index 00000000000..ed6084a7e7b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_expression.php.inc @@ -0,0 +1,20 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/skip_on_expression_from_docblock.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/skip_on_expression_from_docblock.php.inc new file mode 100644 index 00000000000..ace38d5063b --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/skip_on_expression_from_docblock.php.inc @@ -0,0 +1,10 @@ +get(); diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Source/SomeReturnDocblock.php b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Source/SomeReturnDocblock.php new file mode 100644 index 00000000000..eadb55e6b29 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Source/SomeReturnDocblock.php @@ -0,0 +1,15 @@ +var; - /** @var string $oldVariableName */ $oldVariableName = (string) $this->getName($catchVar); $typeShortName = $this->resolveVariableName($catch->types[0]); diff --git a/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php b/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php index 9d0b078cee3..6cdfd81d3a8 100644 --- a/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php +++ b/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php @@ -5,7 +5,10 @@ namespace Rector\DeadCode\PhpDoc; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Stmt\ClassConst; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; @@ -15,6 +18,7 @@ use PHPStan\Type\UnionType; use Rector\DeadCode\PhpDoc\Guard\TemplateTypeRemovalGuard; use Rector\NodeTypeResolver\TypeComparator\TypeComparator; +use Rector\PHPStan\ScopeFetcher; use Rector\StaticTypeMapper\StaticTypeMapper; final readonly class DeadVarTagValueNodeAnalyzer @@ -22,13 +26,13 @@ public function __construct( private TypeComparator $typeComparator, private StaticTypeMapper $staticTypeMapper, - private TemplateTypeRemovalGuard $templateTypeRemovalGuard + private TemplateTypeRemovalGuard $templateTypeRemovalGuard, ) { } - public function isDead(VarTagValueNode $varTagValueNode, Property|ClassConst $property): bool + public function isDead(VarTagValueNode $varTagValueNode, Property|ClassConst|Expression $node): bool { - if (! $property->type instanceof Node) { + if (! $node instanceof Expression && ! $node->type instanceof Node) { return false; } @@ -36,13 +40,40 @@ public function isDead(VarTagValueNode $varTagValueNode, Property|ClassConst $pr return false; } + $targetNode = null; + + if ($node instanceof Expression && $node->expr instanceof Assign) { + $targetNode = $node->expr->expr; + } elseif ($node instanceof Property || $node instanceof ClassConst) { + $targetNode = $node->type; + } + + // allow Identifier, ComplexType, and Name on Property and ClassConst + if (! $targetNode instanceof Node) { + return false; + } + if ($varTagValueNode->type instanceof GenericTypeNode) { return false; } // is strict type superior to doc type? keep strict type only - $propertyType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($property->type); - $docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($varTagValueNode->type, $property); + $propertyType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($targetNode); + $docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($varTagValueNode->type, $node); + + if ($node instanceof Expression) { + $scope = ScopeFetcher::fetch($node); + + // only allow Expr on assign expr + if (! $targetNode instanceof Expr) { + return false; + } + + $nativeType = $scope->getNativeType($targetNode); + if (! $docType->equals($nativeType)) { + return false; + } + } if (! $this->templateTypeRemovalGuard->isLegal($docType)) { return false; @@ -59,9 +90,9 @@ public function isDead(VarTagValueNode $varTagValueNode, Property|ClassConst $pr } if ($this->typeComparator->arePhpParserAndPhpStanPhpDocTypesEqual( - $property->type, + $targetNode, $varTagValueNode->type, - $property + $node )) { return true; } diff --git a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php index 929b496cd77..a33631c9d15 100644 --- a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php +++ b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php @@ -32,7 +32,7 @@ public function __construct( ) { } - public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property|ClassConst $property): bool + public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property|ClassConst|Expression $property): bool { $varTagValueNode = $phpDocInfo->getVarTagValueNode(); if (! $varTagValueNode instanceof VarTagValueNode) { diff --git a/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php b/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php index f3d21ac78de..1c07d2c5419 100644 --- a/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php +++ b/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Stmt\ClassConst; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\DeadCode\PhpDoc\TagRemover\VarTagRemover; @@ -54,11 +55,11 @@ final class SomeClass */ public function getNodeTypes(): array { - return [Property::class, ClassConst::class]; + return [Property::class, ClassConst::class, Expression::class]; } /** - * @param Property|ClassConst $node + * @param Property|ClassConst|Expression $node */ public function refactor(Node $node): ?Node { diff --git a/rules/Php55/RegexMatcher.php b/rules/Php55/RegexMatcher.php index b53cceda040..b9bf582ca43 100644 --- a/rules/Php55/RegexMatcher.php +++ b/rules/Php55/RegexMatcher.php @@ -41,7 +41,6 @@ public function resolvePatternExpressionWithoutEIfFound(Expr $expr): Concat|Stri default => $delimiter }; - /** @var string $modifiers */ $modifiers = $this->resolveModifiers((string) Strings::after($pattern, $delimiter, -1)); if (! \str_contains($modifiers, 'e')) { return null;