Skip to content

feat: add NarrowTooWideReturnTypeRector#7147

Closed
calebdw wants to merge 17 commits intorectorphp:mainfrom
calebdw:calebdw/push-rzmvxsssrszs
Closed

feat: add NarrowTooWideReturnTypeRector#7147
calebdw wants to merge 17 commits intorectorphp:mainfrom
calebdw:calebdw/push-rzmvxsssrszs

Conversation

@calebdw
Copy link
Contributor

@calebdw calebdw commented Aug 15, 2025

Hello!

Closes rectorphp/rector#9309

This adds a rule that automatically narrows too wide return types (helps solve the return.unusedType phpstan errors):

final class SomeClass
{
-   public function getData(): string|int|\DateTime
+   public function getData(): string|int
    {
        if (rand(0, 1)) {
            return 'text';
        }
        return 1000;
    }
}

Thanks!

@calebdw calebdw force-pushed the calebdw/push-rzmvxsssrszs branch from ffa5c1c to 856c8f3 Compare August 15, 2025 05:24
@TomasVotruba
Copy link
Member

Thanks for great rule 👌 Will save a lot of manual work.

The rule name seems negative, like PHPStan rule.
Rector fixes something for better, e.g. NarrowTooWideUnionReturnTypeRector

@calebdw calebdw force-pushed the calebdw/push-rzmvxsssrszs branch 2 times, most recently from d2f8c9b to ca11ae8 Compare August 15, 2025 13:23
@calebdw calebdw force-pushed the calebdw/push-rzmvxsssrszs branch from ca11ae8 to 946abdb Compare August 15, 2025 13:27
@calebdw calebdw force-pushed the calebdw/push-rzmvxsssrszs branch 2 times, most recently from e8e70bb to e138a71 Compare August 15, 2025 13:34
@calebdw calebdw force-pushed the calebdw/push-rzmvxsssrszs branch 2 times, most recently from ec418af to 627b521 Compare August 15, 2025 14:06
@calebdw calebdw changed the title feat: add TooWideReturnTypeRector feat: add NarrowTooWideReturnTypeRector Aug 15, 2025
@calebdw calebdw force-pushed the calebdw/push-rzmvxsssrszs branch 2 times, most recently from 89d59c5 to ecb9c33 Compare August 15, 2025 14:18
@samsonasik
Copy link
Member

samsonasik commented Aug 15, 2025

Please:

  • don't use force push except needed (need rebase due to main branch updated), it is hard to review the updated code. Just add more commits
  • don't close conversation except actually resolved after new commit.

@calebdw calebdw force-pushed the calebdw/push-rzmvxsssrszs branch from 2af5b38 to 7558829 Compare August 16, 2025 01:16
@samsonasik
Copy link
Member

samsonasik commented Aug 16, 2025

I think @return docblock should be updated as well if exists:

/**
- * @return \Iterator<int>|string
+ * @return \Iterator<int>
 */
-function foo(): \Iterator|string
+function foo(): \Iterator
{
   return new \ArrayIterator([1]);
}

You can use :

$this->phpDocTypeChanger->changeReturnType($node, $phpDocInfo, $newType);

@calebdw
Copy link
Contributor Author

calebdw commented Aug 16, 2025

I think @return docblock should be updated as well if exists:

I imagine that I can longer just use getNativeType() then

@samsonasik
Copy link
Member

getType() still needed as you extract UnionType, that's can be identifier, eg: string

@calebdw
Copy link
Contributor Author

calebdw commented Aug 16, 2025

I'm not too clear on the difference, but I imagine that getNativeType does not include generic information?

@samsonasik
Copy link
Member

samsonasik commented Aug 16, 2025

getType() still needed as you extract UnionType, that's can be identifier, eg: string, here:

        $types = $returnType instanceof UnionType
            ? $returnType->types
            : [$returnType->type, new Identifier('null')];
        $usedTypes = [];

        foreach ($types as $type) {
            $declaredType = $type instanceof Expr
                ? $this->nodeTypeResolver->getNativeType($type)
                : $this->getType($type);

The $returnType->types can be collected of Name or Identifier, not Expr.

@calebdw calebdw force-pushed the calebdw/push-rzmvxsssrszs branch from a3111e2 to 6837fd4 Compare August 16, 2025 21:32
@samsonasik
Copy link
Member

samsonasik commented Aug 17, 2025

Please add fixture to skip docblock base type:

/**
 * @param int $a
 */
function foo($a): int|string
{
    return $a;
}

@samsonasik
Copy link
Member

Please add test for this:

    /**
     * @param int $a
     * @return int|string 
     */
    function mixedReturn($a): int|string
    {
        return $a;
    }

* @param class-string<SomeInterface> $class
* @return class-string<SomeInterface>|int
*/
public function bar(string $class): string|int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add fixture to skip non-typed based then, to skip this:

    /**
     * @param class-string<SomeInterface> $class
     * @return class-string<SomeInterface>|int
     */
    public function bar($class): string|int
    {
        return $class;
    }

Above is marked as MixedType on its type, see

https://phpstan.org/r/3e952cb5-c876-4b6e-bfef-89aa5d5b942b

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see no reason to do that, this signature is enforced by PHPStan and trying to loop over all the parameter types is only going to unnecessarily complicate the code

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's needed, upgrading to native type is only when we sure about this, when unsure, just skip :)

We had docblock based changing rules in the past, but mostly cause issues in projects, that's why using native type as posible should be used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expr returned is not always based on param, see my latest comments :)

@calebdw calebdw force-pushed the calebdw/push-rzmvxsssrszs branch from 441b483 to a9e401f Compare August 17, 2025 01:48
@samsonasik
Copy link
Member

Please add fixture to skip this, which actually skip MixedType:

    /**
     * @return class-string<\stdClass>|int
     */
    public function skipMixedByDoc(): string|int
    {
        /**
         * @var class-string<\stdClass> $class
         */
        $class = get();
        return $class;
    }

see https://phpstan.org/r/47037a56-f5da-4e99-bb3d-7adc3b609612 that it still mixed.

@samsonasik
Copy link
Member

@calebdw sorry, I am going to close this, as you don't follow my guideline and suggestion. I am here for the barricade to avoid the new rule cause issues in our project.

Take your time to understand our philosophy to take upgrade as safe as possible.

Thank you for understanding

@github-actions
Copy link
Contributor

This pull request has been automatically locked because it has been closed for 150 days. Please open a new PR if you want to continue the work.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rector to automatically remove return type that the function never returns

3 participants