Fix phpstan/phpstan#14249: Propagate @phpstan-assert-* annotations to First-class callables (Closures) and string callables#5201
Conversation
|
Stupid fix, should be a a new getAssertions method on CallableParametersAcceptor. |
…ring callables - Added Assertions support to ClosureType so first-class callables preserve assertion metadata - Modified InitializerExprTypeResolver::createFirstClassCallable() to pass assertions from function/method reflection to the created ClosureType - Added specifyTypesFromCallableCall() in TypeSpecifier to handle variable function calls ($f($v)) by extracting assertions from ClosureType or resolving string callables to their function reflection - New regression test in tests/PHPStan/Analyser/nsrt/bug-14249.php Closes phpstan/phpstan#14249
9d54f40 to
61b5d3e
Compare
staabm
left a comment
There was a problem hiding this comment.
revert the fix and implement a new getAssertions method on CallableParametersAcceptor instead
|
In case it doesn't notice, you need to rebase the branch |
Move assertion retrieval from ClosureType-specific code to the CallableParametersAcceptor interface, so TypeSpecifier can use the interface method instead of instanceof ClosureType checks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Done. Here's a summary of the changes: Added Files changed:
|
cb0a806 to
942f48a
Compare
942f48a to
7467880
Compare
staabm
left a comment
There was a problem hiding this comment.
template type resolving does not work yet for first class callable functions. see the newly added failling test case.
fix it
Use ParametersAcceptorSelector::selectFromArgs() to resolve template types from actual call arguments before applying assertions, matching the pattern used for direct function calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Done. Here's what I fixed: Problem: Template type resolving wasn't working for first-class callable ( Fix: Changed The resolved acceptor's |
|
phpstan/phpunit test-failures will be fixed with phpstan/phpstan-phpunit#287 (unrelated to this PR). I think this one here is good to go |
|
Thank you! |
|
I processed this review but have nothing to report. |
Summary
When a function with
@phpstan-assert-if-true(or similar assertion annotations) was converted to a first-class callable (fn(...)) or used as a string callable ('fn_name'), the assertion metadata was lost. This meant type narrowing did not work when calling the function through a variable.Changes
Assertionsproperty toClosureType(src/Type/ClosureType.php) with a constructor parameter and getter, preserving assertions throughtraverse()andtraverseSimultaneously()InitializerExprTypeResolver::createFirstClassCallable()(src/Reflection/InitializerExprTypeResolver.php) to pass the function/method's assertions to the createdClosureTypespecifyTypesFromCallableCall()method and a new branch inTypeSpecifier::specifyTypesInCondition()(src/Analyser/TypeSpecifier.php) to handleFuncCallnodes where the callee is a variable — it checks forClosureTypewith assertions (first-class callables) andConstantStringTypecallables (string callables), then applies assertions using the existingspecifyTypesFromAssertsmechanismRoot cause
The
TypeSpecifieronly handled assertion propagation for direct function calls (FuncCallwith aNamenode). When a function was used as a first-class callable (is_positive_int(...)), it was converted to aClosureTypethat did not carry assertion metadata. Similarly, string callables ('is_positive_int') were not resolved back to their function reflection for assertion processing. The fix adds assertion storage toClosureTypeand extendsTypeSpecifierto resolve assertions from both callable forms.Test
Added
tests/PHPStan/Analyser/nsrt/bug-14249.php— an NSRT test that verifies@phpstan-assert-if-true positive-int $valuecorrectly narrows the type toint<1, max>when the function is called directly, through a first-class callable variable, and through a string callable variable.Fixes phpstan/phpstan#14249