Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Skip function-like and anonymous class nodes when checking if a function or method is a generator #3794

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sebkehr wants to merge 3 commits into phpstan:2.1.x
base: 2.1.x
Choose a base branch
Loading
from sport-iat:improved_return_type_checks_wrt_yielding_closures

Conversation

@sebkehr
Copy link

@sebkehr sebkehr commented Jan 23, 2025

Fixes: phpstan/phpstan#12462.

As is, when checking if a function (or method) is a generator (via PhpFunctionFromParserNodeReflection::isGenerator()) all subnodes are recursively checked for containment of a yield expression. Hence any function or method itself returning a yielding closure or arrow function is falsely determined to be a generator which in turn leads to incorrect return type checks as demonstrated in https://phpstan.org/r/5970e2e2-0aad-4927-8938-3f7d83e73a95.

The proposed fix therefore simply skips FunctionLike nodes in the above mentiond recursive check.

Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

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

sebkehr reacted with thumbs up emoji

if ($nodeProperty instanceof Node && $this->nodeIsOrContainsYield($nodeProperty)) {
if ($nodeProperty instanceof Node &&
!$nodeProperty instanceof FunctionLike &&
Copy link
Member

@ondrejmirtes ondrejmirtes Jan 23, 2025

Choose a reason for hiding this comment

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

Would be great to also not descend into anonymous classes.

sebkehr reacted with thumbs up emoji
Copy link
Author

Choose a reason for hiding this comment

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

@ondrejmirtes Anonymous class nodes should now be skipped as well. I submitted the fix via Roave/BetterReflection#1479.

@sebkehr sebkehr force-pushed the improved_return_type_checks_wrt_yielding_closures branch from 710646b to e75270a Compare January 25, 2025 08:55
@sebkehr sebkehr force-pushed the improved_return_type_checks_wrt_yielding_closures branch from e75270a to 13d7837 Compare January 25, 2025 09:49
@sebkehr sebkehr changed the title (削除) Skip function-like nodes when checking if a function or method is a generator (削除ここまで) (追記) Skip function-like and anonymous class nodes when checking if a function or method is a generator (追記ここまで) Jan 25, 2025
Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

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

I just realized there's one more place that needs fixing. Check out CleaningParserTest. The job of CleaningParser is to erase function bodies of not-analyzed code (like in vendor/) before the AST is cached into memory.

I worry the code is faulty and it might do something like this:

Before:

function foo() {
 $fn = fn () => yield;
};

After:

function foo() {
 yield;
};

Changing function foo() from non-generator to a generator.

Thank you!

Copy link
Author

sebkehr commented Jan 26, 2025

I just realized there's one more place that needs fixing. Check out CleaningParserTest. The job of CleaningParser is to erase function bodies of not-analyzed code (like in vendor/) before the AST is cached into memory.

Ah ok, I'll look into it.

@sebkehr sebkehr force-pushed the improved_return_type_checks_wrt_yielding_closures branch 5 times, most recently from d446d6c to e046593 Compare January 30, 2025 07:11
Copy link
Author

sebkehr commented Jan 30, 2025

@ondrejmirtes

I added two more examples to tests/PHPStan/Parser/data/cleaning-1-before.php. Please also have a look on the expected outcome after cleaning in tests/PHPStan/Parser/data/cleaning-1-after.php.

With the proposed changes to src/Parser/CleaningVisitor.php the closure in ContainsClosure::doFoo() will now be erased not leading to any left-over yield statements just like the yielding array-function in ContainsClosure::doBar(). Is this the intended behavior?

I also added Baz::someGenerator3() containing yield as a subexpression which will now lead to a yield expression after cleaning.

@sebkehr sebkehr force-pushed the improved_return_type_checks_wrt_yielding_closures branch from e046593 to 866e7cb Compare February 13, 2025 15:15
@sebkehr sebkehr force-pushed the improved_return_type_checks_wrt_yielding_closures branch 5 times, most recently from 8840a07 to f88659c Compare February 25, 2025 12:47
@sebkehr sebkehr force-pushed the improved_return_type_checks_wrt_yielding_closures branch 2 times, most recently from df9dc44 to d4fec47 Compare February 27, 2025 07:58
@sebkehr sebkehr force-pushed the improved_return_type_checks_wrt_yielding_closures branch from d4fec47 to 390646b Compare March 13, 2025 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@ondrejmirtes ondrejmirtes Awaiting requested review from ondrejmirtes

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Incorrect return type checks with yielding closures

AltStyle によって変換されたページ (->オリジナル) /