Skip to content

First-class callable crash on Expr methods#735

Merged
staabm merged 1 commit intophpstan:2.0.xfrom
simPod:fix/first-class-callable-expr-crash
Mar 10, 2026
Merged

First-class callable crash on Expr methods#735
staabm merged 1 commit intophpstan:2.0.xfrom
simPod:fix/first-class-callable-expr-crash

Conversation

@simPod
Copy link
Contributor

@simPod simPod commented Mar 8, 2026

Fixes #711

Repro https://github.com/phpstan/phpstan-doctrine/actions/runs/22821964729/job/66196045235?pr=735#step:9:28

When first-class callable syntax ($expr->in(...)) is used inside match arms or assigned to variables, PHPStan resolves the closure and visits the extension with a MethodCall where isFirstClassCallable() is false and getArgs() is empty. This causes the actual Expr method to be called with 0 arguments, crashing with ArgumentCountError.

Fix with try/catch Throwable around the reflective method invocation for indirect cases (match arms, variable assignments). We also need to cover TypeError from ORM 3.x's typed signatures, plus any other unexpected exception from the reflective call.

@simPod simPod force-pushed the fix/first-class-callable-expr-crash branch 3 times, most recently from 238be97 to 1821cee Compare March 8, 2026 13:39
@simPod simPod marked this pull request as ready for review March 8, 2026 13:41
Comment on lines +26 to +28
if (PHP_VERSION_ID < 80100) {
self::markTestSkipped('Test requires PHP 8.1.');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

#[RequiresPhp('>= 8.1')]

maybe ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not available in phpunit v9

@simPod simPod force-pushed the fix/first-class-callable-expr-crash branch 2 times, most recently from a971cdb to 2dbaea3 Compare March 9, 2026 09:33
@simPod simPod requested a review from VincentLanglet March 9, 2026 09:37
@simPod simPod force-pushed the fix/first-class-callable-expr-crash branch from 2dbaea3 to a23654a Compare March 9, 2026 10:44
Comment on lines +59 to +64
try {
$exprValue = $expr->{$methodReflection->getName()}(...$args);
} catch (Throwable $e) {
return null;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

this change is not covered by a test it seems

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@simPod simPod force-pushed the fix/first-class-callable-expr-crash branch from a23654a to 6a70cb9 Compare March 9, 2026 17:03
@simPod simPod requested a review from staabm March 10, 2026 15:07
@staabm
Copy link
Contributor

staabm commented Mar 10, 2026

thank you!

When first-class callable syntax ($expr->in(...)) is used inside match
arms or assigned to variables, PHPStan resolves the closure and visits
the extension with a MethodCall where isFirstClassCallable() is false
and getArgs() is empty. This causes the actual Expr method to be called
with 0 arguments, crashing with ArgumentCountError.

Catch Throwable (not just ArgumentCountError) around the reflective
method invocation to also handle TypeError from typed signatures in
ORM 3.x and other potential exceptions.

Closes phpstan#711

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@staabm staabm force-pushed the fix/first-class-callable-expr-crash branch from 6a70cb9 to ef3d5a1 Compare March 10, 2026 16:52
@staabm staabm merged commit 27cd48b into phpstan:2.0.x Mar 10, 2026
81 of 83 checks passed
@simPod simPod deleted the fix/first-class-callable-expr-crash branch March 10, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash with phpstan 2.1.32

3 participants