From f047b1a7d6e218a3e42a8b2c3ddcbdc49948e3d7 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 12 Oct 2026 10:34:23 +0200 Subject: [PATCH 1/4] [PHPStan] Add PreferGenericTemplateOnReturnedUnionParamRule to spot hidden generics --- phpstan.neon | 1 + .../RemoveNullTagValueNodeRector.php | 5 + .../Break_/RemoveZeroBreakContinueRector.php | 5 + ...enericTemplateOnReturnedUnionParamRule.php | 187 ++++++++++++++++++ ...icTemplateOnReturnedUnionParamRuleTest.php | 32 +++ .../Source/BroadReturnOfUnionParam.php | 21 ++ .../Source/SkipBroadReturn.php | 61 ++++++ 7 files changed, 312 insertions(+) create mode 100644 utils/phpstan/src/Rule/PreferGenericTemplateOnReturnedUnionParamRule.php create mode 100644 utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/PreferGenericTemplateOnReturnedUnionParamRuleTest.php create mode 100644 utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/BroadReturnOfUnionParam.php create mode 100644 utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/SkipBroadReturn.php diff --git a/phpstan.neon b/phpstan.neon index c80acc14b90..de86fa95428 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,6 +5,7 @@ rules: - Rector\Utils\PHPStan\Rule\RegisterRelatedPolyfillRectorRule - Rector\Utils\PHPStan\Rule\NoScopeFetchBeforeCheapGuardRule - Rector\Utils\PHPStan\Rule\PreferGenericTemplateOnSameUnionTypeRule + - Rector\Utils\PHPStan\Rule\PreferGenericTemplateOnReturnedUnionParamRule parameters: level: 8 diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php index 6430df91de8..ea8648de701 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php @@ -159,6 +159,11 @@ static function (AstNode $astNode) use ($paramNames): ?int { ); } + /** + * @template TNode of Expression|Property + * @param TNode $node + * @return TNode|null + */ private function processVarTagNull(Expression|Property $node): ?Node { $phpdocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); diff --git a/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php b/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php index efe0f99f6c6..3b936de5e3d 100644 --- a/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php +++ b/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php @@ -110,6 +110,11 @@ public function refactor(Node $node): ?Node return null; } + /** + * @template TNode of Break_|Continue_ + * @param TNode $stmt + * @return TNode|null + */ private function processVariableNum(Break_|Continue_ $stmt, Variable $numVariable): ?Node { $staticType = $this->getType($numVariable); diff --git a/utils/phpstan/src/Rule/PreferGenericTemplateOnReturnedUnionParamRule.php b/utils/phpstan/src/Rule/PreferGenericTemplateOnReturnedUnionParamRule.php new file mode 100644 index 00000000000..08f1ba25e91 --- /dev/null +++ b/utils/phpstan/src/Rule/PreferGenericTemplateOnReturnedUnionParamRule.php @@ -0,0 +1,187 @@ + + * @see \Rector\Utils\PHPStan\Tests\Rule\PreferGenericTemplateOnReturnedUnionParamRule\PreferGenericTemplateOnReturnedUnionParamRuleTest + */ +final class PreferGenericTemplateOnReturnedUnionParamRule implements Rule +{ + private const string ERROR_MESSAGE = 'Return type "%s" is broader than the returned union param "%s". Add a @template generic to narrow the return type to the passed one.'; + + public function getNodeType(): string + { + return ClassMethod::class; + } + + /** + * @param ClassMethod $node + * @return list + */ + public function processNode(Node $node, Scope $scope): array + { + $docComment = $node->getDocComment(); + if ($docComment instanceof Doc && str_contains($docComment->getText(), '@template')) { + return []; + } + + $returnName = $this->resolveSingleClassReturnName($node->returnType); + if ($returnName === null) { + return []; + } + + foreach ($node->params as $param) { + $paramNames = $this->resolveClassUnionNames($param->type); + if ($paramNames === null) { + continue; + } + + // return type is one of the union members, not a broad shared parent - a generic does not apply + if (in_array($returnName, $paramNames, true)) { + continue; + } + + if (! $param->var instanceof Variable || ! is_string($param->var->name)) { + continue; + } + + if (! $this->methodReturnsVariable($node, $param->var->name)) { + continue; + } + + $shortNames = array_map(static function (string $name): string { + $parts = explode('\\', $name); + return end($parts); + }, $paramNames); + + return [ + RuleErrorBuilder::message(sprintf(self::ERROR_MESSAGE, $returnName, implode('|', $shortNames))) + ->identifier('rector.genericTemplateOverBroadReturn') + ->line($node->getStartLine()) + ->build(), + ]; + } + + return []; + } + + private function methodReturnsVariable(ClassMethod $classMethod, string $variableName): bool + { + if ($classMethod->stmts === null) { + return false; + } + + $foundReturn = new NodeFinder()->findFirst( + $classMethod->stmts, + static fn (Node $subNode): bool => $subNode instanceof Return_ + && $subNode->expr instanceof Variable + && $subNode->expr->name === $variableName + ); + + return $foundReturn instanceof Return_; + } + + /** + * Returns the single class return type name (nullable allowed), or null when the return type is + * missing, scalar, or a union of 2+ class types - the latter is covered by the same-union rule. + */ + private function resolveSingleClassReturnName(null|Identifier|Name|ComplexType $type): ?string + { + if ($type instanceof NullableType) { + $type = $type->type; + } + + if ($type instanceof Name) { + return $type->toString(); + } + + if (! $type instanceof UnionType) { + return null; + } + + $names = []; + foreach ($type->types as $memberType) { + if ($memberType instanceof Identifier) { + if ($memberType->toLowerString() === 'null') { + continue; + } + + return null; + } + + if (! $memberType instanceof Name) { + return null; + } + + $names[] = $memberType->toString(); + } + + if (count($names) !== 1) { + return null; + } + + return $names[0]; + } + + /** + * Returns sorted class-type member names (null stripped), or null when the type is not a + * union of 2+ class types - scalars, array and intersections disqualify it. + * + * @return list|null + */ + private function resolveClassUnionNames(null|Identifier|Name|ComplexType $type): ?array + { + if (! $type instanceof UnionType) { + return null; + } + + $names = []; + foreach ($type->types as $memberType) { + if ($memberType instanceof Identifier) { + if ($memberType->toLowerString() === 'null') { + continue; + } + + return null; + } + + if (! $memberType instanceof Name) { + return null; + } + + $names[] = $memberType->toString(); + } + + if (count($names) < 2) { + return null; + } + + $names = array_unique($names); + sort($names); + + return $names; + } +} diff --git a/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/PreferGenericTemplateOnReturnedUnionParamRuleTest.php b/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/PreferGenericTemplateOnReturnedUnionParamRuleTest.php new file mode 100644 index 00000000000..0c997db0d6b --- /dev/null +++ b/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/PreferGenericTemplateOnReturnedUnionParamRuleTest.php @@ -0,0 +1,32 @@ + + */ +final class PreferGenericTemplateOnReturnedUnionParamRuleTest extends RuleTestCase +{ + public function testBroadReturnOfUnionParam(): void + { + $expectedErrorMessage = 'Return type "PhpParser\Node" is broader than the returned union param "MethodCall|New_". Add a @template generic to narrow the return type to the passed one.'; + + $this->analyse([__DIR__ . '/Source/BroadReturnOfUnionParam.php'], [[$expectedErrorMessage, 13]]); + } + + public function testSkip(): void + { + $this->analyse([__DIR__ . '/Source/SkipBroadReturn.php'], []); + } + + protected function getRule(): Rule + { + return new PreferGenericTemplateOnReturnedUnionParamRule(); + } +} diff --git a/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/BroadReturnOfUnionParam.php b/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/BroadReturnOfUnionParam.php new file mode 100644 index 00000000000..28f96f854db --- /dev/null +++ b/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/BroadReturnOfUnionParam.php @@ -0,0 +1,21 @@ +var; + } + + if (! $expr instanceof Variable) { + return null; + } + + return $expr; + } +} From 562cb03b1446a6191d21e7013cbc0b939650d055 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 12 Oct 2026 10:35:58 +0200 Subject: [PATCH 2/4] skip new test source fixtures in class-leak --- .github/workflows/code_analysis.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index c1d643e8e13..8ce4e369e44 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -53,7 +53,7 @@ jobs: - name: 'Active Classes' run: | - vendor/bin/class-leak check bin config src rules utils --skip-suffix "Rector" --skip-type="Rector\\Utils\\Compiler\\Unprefixer" --skip-type="Rector\\NodeCollector\\BinaryOpConditionsCollector" --skip-type="Rector\\DependencyInjection\\PHPStan\\RichParserFactory" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnSameUnionTypeRule\\Source\\SameUnionParamAndReturn" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnSameUnionTypeRule\\Source\\SkipGenericTemplate" + vendor/bin/class-leak check bin config src rules utils --skip-suffix "Rector" --skip-type="Rector\\Utils\\Compiler\\Unprefixer" --skip-type="Rector\\NodeCollector\\BinaryOpConditionsCollector" --skip-type="Rector\\DependencyInjection\\PHPStan\\RichParserFactory" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnSameUnionTypeRule\\Source\\SameUnionParamAndReturn" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnSameUnionTypeRule\\Source\\SkipGenericTemplate" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnReturnedUnionParamRule\\Source\\BroadReturnOfUnionParam" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnReturnedUnionParamRule\\Source\\SkipBroadReturn" - name: 'Compatible PHPStan versions' From 572845211474ea9a83c810f861d08cd18226a8d4 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 12 Oct 2026 10:38:20 +0200 Subject: [PATCH 3/4] simplify class-leak: skip Source fixture dirs via skip-path instead of per-class --- .github/workflows/code_analysis.yaml | 2 +- .../Rector/ClassMethod/RemoveNullTagValueNodeRector.php | 2 +- rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index 8ce4e369e44..831ebb1012f 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -53,7 +53,7 @@ jobs: - name: 'Active Classes' run: | - vendor/bin/class-leak check bin config src rules utils --skip-suffix "Rector" --skip-type="Rector\\Utils\\Compiler\\Unprefixer" --skip-type="Rector\\NodeCollector\\BinaryOpConditionsCollector" --skip-type="Rector\\DependencyInjection\\PHPStan\\RichParserFactory" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnSameUnionTypeRule\\Source\\SameUnionParamAndReturn" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnSameUnionTypeRule\\Source\\SkipGenericTemplate" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnReturnedUnionParamRule\\Source\\BroadReturnOfUnionParam" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\PreferGenericTemplateOnReturnedUnionParamRule\\Source\\SkipBroadReturn" + vendor/bin/class-leak check bin config src rules utils --skip-suffix "Rector" --skip-type="Rector\\Utils\\Compiler\\Unprefixer" --skip-type="Rector\\NodeCollector\\BinaryOpConditionsCollector" --skip-type="Rector\\DependencyInjection\\PHPStan\\RichParserFactory" --skip-path="Source" - name: 'Compatible PHPStan versions' diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php index ea8648de701..45003a9deb4 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php @@ -164,7 +164,7 @@ static function (AstNode $astNode) use ($paramNames): ?int { * @param TNode $node * @return TNode|null */ - private function processVarTagNull(Expression|Property $node): ?Node + private function processVarTagNull(Expression|Property $node): Expression|Property|null { $phpdocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); $varTagValueNode = $phpdocInfo->getVarTagValueNode(); diff --git a/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php b/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php index 3b936de5e3d..0aab61a89e6 100644 --- a/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php +++ b/rules/Php54/Rector/Break_/RemoveZeroBreakContinueRector.php @@ -115,7 +115,7 @@ public function refactor(Node $node): ?Node * @param TNode $stmt * @return TNode|null */ - private function processVariableNum(Break_|Continue_ $stmt, Variable $numVariable): ?Node + private function processVariableNum(Break_|Continue_ $stmt, Variable $numVariable): Break_|Continue_|null { $staticType = $this->getType($numVariable); From dd582dc77636fc5d5dbb8d1b3f450f70e6f91016 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 12 Oct 2026 10:49:09 +0200 Subject: [PATCH 4/4] rename rule to NarrowReturnToUnionParamRule, suggest explicit union return instead of generic --- phpstan.neon | 2 +- .../ArgumentDefaultValueReplacer.php | 2 +- ...e.php => NarrowReturnToUnionParamRule.php} | 40 ++++++++++++------- .../NarrowReturnToUnionParamRuleTest.php | 32 +++++++++++++++ .../Source/BroadReturnOfUnionParam.php | 2 +- .../Source/SkipBroadReturn.php | 14 +------ ...icTemplateOnReturnedUnionParamRuleTest.php | 32 --------------- 7 files changed, 63 insertions(+), 61 deletions(-) rename utils/phpstan/src/Rule/{PreferGenericTemplateOnReturnedUnionParamRule.php => NarrowReturnToUnionParamRule.php} (80%) create mode 100644 utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/NarrowReturnToUnionParamRuleTest.php rename utils/phpstan/tests/Rule/{PreferGenericTemplateOnReturnedUnionParamRule => NarrowReturnToUnionParamRule}/Source/BroadReturnOfUnionParam.php (77%) rename utils/phpstan/tests/Rule/{PreferGenericTemplateOnReturnedUnionParamRule => NarrowReturnToUnionParamRule}/Source/SkipBroadReturn.php (70%) delete mode 100644 utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/PreferGenericTemplateOnReturnedUnionParamRuleTest.php diff --git a/phpstan.neon b/phpstan.neon index de86fa95428..2c146381ddb 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,7 +5,7 @@ rules: - Rector\Utils\PHPStan\Rule\RegisterRelatedPolyfillRectorRule - Rector\Utils\PHPStan\Rule\NoScopeFetchBeforeCheapGuardRule - Rector\Utils\PHPStan\Rule\PreferGenericTemplateOnSameUnionTypeRule - - Rector\Utils\PHPStan\Rule\PreferGenericTemplateOnReturnedUnionParamRule + - Rector\Utils\PHPStan\Rule\NarrowReturnToUnionParamRule parameters: level: 8 diff --git a/rules/Arguments/ArgumentDefaultValueReplacer.php b/rules/Arguments/ArgumentDefaultValueReplacer.php index 16527bc1502..3e92627089a 100644 --- a/rules/Arguments/ArgumentDefaultValueReplacer.php +++ b/rules/Arguments/ArgumentDefaultValueReplacer.php @@ -105,7 +105,7 @@ private function processParams( private function processArgs( MethodCall|StaticCall|FuncCall|New_ $expr, ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue - ): ?Expr { + ): MethodCall|StaticCall|FuncCall|New_|null { if ($expr->isFirstClassCallable()) { return null; } diff --git a/utils/phpstan/src/Rule/PreferGenericTemplateOnReturnedUnionParamRule.php b/utils/phpstan/src/Rule/NarrowReturnToUnionParamRule.php similarity index 80% rename from utils/phpstan/src/Rule/PreferGenericTemplateOnReturnedUnionParamRule.php rename to utils/phpstan/src/Rule/NarrowReturnToUnionParamRule.php index 08f1ba25e91..337222e98e8 100644 --- a/utils/phpstan/src/Rule/PreferGenericTemplateOnReturnedUnionParamRule.php +++ b/utils/phpstan/src/Rule/NarrowReturnToUnionParamRule.php @@ -4,11 +4,11 @@ namespace Rector\Utils\PHPStan\Rule; -use PhpParser\Comment\Doc; use PhpParser\Node; use PhpParser\Node\ComplexType; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; +use PhpParser\Node\IntersectionType; use PhpParser\Node\Name; use PhpParser\Node\NullableType; use PhpParser\Node\Stmt\ClassMethod; @@ -22,15 +22,15 @@ /** * Flags a method that takes a class-type union param (A|B) but declares a broader single return type - * (e.g. their shared parent Node), then returns that very param - a hidden generic. - * A @template generic narrows the return to the passed type instead of the broad parent. + * (e.g. their shared parent Node), then returns that very param. + * The return type should be the explicit union the param carries, not the broad parent. * * @implements Rule - * @see \Rector\Utils\PHPStan\Tests\Rule\PreferGenericTemplateOnReturnedUnionParamRule\PreferGenericTemplateOnReturnedUnionParamRuleTest + * @see \Rector\Utils\PHPStan\Tests\Rule\NarrowReturnToUnionParamRule\NarrowReturnToUnionParamRuleTest */ -final class PreferGenericTemplateOnReturnedUnionParamRule implements Rule +final class NarrowReturnToUnionParamRule implements Rule { - private const string ERROR_MESSAGE = 'Return type "%s" is broader than the returned union param "%s". Add a @template generic to narrow the return type to the passed one.'; + private const string ERROR_MESSAGE = 'Return type "%s" is too broad for a returned union param. Declare the explicit union return type "%s" instead.'; public function getNodeType(): string { @@ -43,11 +43,6 @@ public function getNodeType(): string */ public function processNode(Node $node, Scope $scope): array { - $docComment = $node->getDocComment(); - if ($docComment instanceof Doc && str_contains($docComment->getText(), '@template')) { - return []; - } - $returnName = $this->resolveSingleClassReturnName($node->returnType); if ($returnName === null) { return []; @@ -59,7 +54,7 @@ public function processNode(Node $node, Scope $scope): array continue; } - // return type is one of the union members, not a broad shared parent - a generic does not apply + // return type is one of the union members, not a broad shared parent - already narrow enough if (in_array($returnName, $paramNames, true)) { continue; } @@ -77,9 +72,13 @@ public function processNode(Node $node, Scope $scope): array return end($parts); }, $paramNames); + if ($this->isNullableReturnType($node->returnType)) { + $shortNames[] = 'null'; + } + return [ RuleErrorBuilder::message(sprintf(self::ERROR_MESSAGE, $returnName, implode('|', $shortNames))) - ->identifier('rector.genericTemplateOverBroadReturn') + ->identifier('rector.explicitUnionReturnType') ->line($node->getStartLine()) ->build(), ]; @@ -104,9 +103,22 @@ private function methodReturnsVariable(ClassMethod $classMethod, string $variabl return $foundReturn instanceof Return_; } + private function isNullableReturnType(null|Identifier|Name|ComplexType $type): bool + { + if ($type instanceof NullableType) { + return true; + } + + if (! $type instanceof UnionType) { + return false; + } + + return array_any($type->types, fn (Identifier|Name|IntersectionType $memberType): bool => $memberType instanceof Identifier && $memberType->toLowerString() === 'null'); + } + /** * Returns the single class return type name (nullable allowed), or null when the return type is - * missing, scalar, or a union of 2+ class types - the latter is covered by the same-union rule. + * missing, scalar, or a union of 2+ class types - the latter is already an explicit union. */ private function resolveSingleClassReturnName(null|Identifier|Name|ComplexType $type): ?string { diff --git a/utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/NarrowReturnToUnionParamRuleTest.php b/utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/NarrowReturnToUnionParamRuleTest.php new file mode 100644 index 00000000000..71c588cdaa5 --- /dev/null +++ b/utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/NarrowReturnToUnionParamRuleTest.php @@ -0,0 +1,32 @@ + + */ +final class NarrowReturnToUnionParamRuleTest extends RuleTestCase +{ + public function testBroadReturnOfUnionParam(): void + { + $expectedErrorMessage = 'Return type "PhpParser\Node" is too broad for a returned union param. Declare the explicit union return type "MethodCall|New_|null" instead.'; + + $this->analyse([__DIR__ . '/Source/BroadReturnOfUnionParam.php'], [[$expectedErrorMessage, 13]]); + } + + public function testSkip(): void + { + $this->analyse([__DIR__ . '/Source/SkipBroadReturn.php'], []); + } + + protected function getRule(): Rule + { + return new NarrowReturnToUnionParamRule(); + } +} diff --git a/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/BroadReturnOfUnionParam.php b/utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/Source/BroadReturnOfUnionParam.php similarity index 77% rename from utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/BroadReturnOfUnionParam.php rename to utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/Source/BroadReturnOfUnionParam.php index 28f96f854db..b7c7b049fca 100644 --- a/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/BroadReturnOfUnionParam.php +++ b/utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/Source/BroadReturnOfUnionParam.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Utils\PHPStan\Tests\Rule\PreferGenericTemplateOnReturnedUnionParamRule\Source; +namespace Rector\Utils\PHPStan\Tests\Rule\NarrowReturnToUnionParamRule\Source; use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; diff --git a/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/SkipBroadReturn.php b/utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/Source/SkipBroadReturn.php similarity index 70% rename from utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/SkipBroadReturn.php rename to utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/Source/SkipBroadReturn.php index 590d417b2e7..8f1de2682c0 100644 --- a/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/Source/SkipBroadReturn.php +++ b/utils/phpstan/tests/Rule/NarrowReturnToUnionParamRule/Source/SkipBroadReturn.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Utils\PHPStan\Tests\Rule\PreferGenericTemplateOnReturnedUnionParamRule\Source; +namespace Rector\Utils\PHPStan\Tests\Rule\NarrowReturnToUnionParamRule\Source; use PhpParser\Node; use PhpParser\Node\Expr\ArrayDimFetch; @@ -12,16 +12,6 @@ final class SkipBroadReturn { - /** - * @template TNode of New_|MethodCall - * @param TNode $node - * @return TNode - */ - public function alreadyGeneric(New_|MethodCall $node): Node - { - return $node; - } - public function doesNotReturnParam(New_|MethodCall $node): ?Node { if ($node instanceof New_) { @@ -41,7 +31,7 @@ public function singleClassParam(New_ $node): ?Node return $node; } - public function unionReturn(New_|MethodCall $node): New_|MethodCall|null + public function alreadyExplicitUnionReturn(New_|MethodCall $node): New_|MethodCall|null { return $node; } diff --git a/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/PreferGenericTemplateOnReturnedUnionParamRuleTest.php b/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/PreferGenericTemplateOnReturnedUnionParamRuleTest.php deleted file mode 100644 index 0c997db0d6b..00000000000 --- a/utils/phpstan/tests/Rule/PreferGenericTemplateOnReturnedUnionParamRule/PreferGenericTemplateOnReturnedUnionParamRuleTest.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ -final class PreferGenericTemplateOnReturnedUnionParamRuleTest extends RuleTestCase -{ - public function testBroadReturnOfUnionParam(): void - { - $expectedErrorMessage = 'Return type "PhpParser\Node" is broader than the returned union param "MethodCall|New_". Add a @template generic to narrow the return type to the passed one.'; - - $this->analyse([__DIR__ . '/Source/BroadReturnOfUnionParam.php'], [[$expectedErrorMessage, 13]]); - } - - public function testSkip(): void - { - $this->analyse([__DIR__ . '/Source/SkipBroadReturn.php'], []); - } - - protected function getRule(): Rule - { - return new PreferGenericTemplateOnReturnedUnionParamRule(); - } -}