diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-07-23 22:27:02 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-07-23 22:27:02 +0900 |
| commit | 2d89dc761237b055ddb8c6dc817c46c0cd6dd44d (patch) | |
| tree | 549ab0605564aeee40b499c0592a6a119e3877f7 /scripts/plugin-class-classifier/src/SourceParser.php | |
| parent | cec38036d6b3d5a4bd009f2eef3bd88791ab5828 (diff) | |
| download | php-shirabe-2d89dc761237b055ddb8c6dc817c46c0cd6dd44d.tar.gz php-shirabe-2d89dc761237b055ddb8c6dc817c46c0cd6dd44d.tar.zst php-shirabe-2d89dc761237b055ddb8c6dc817c46c0cd6dd44d.zip | |
fix(plugin-class-classifier): reach docblock-only generic payload types
The reachability closure only consults @phpstan-return/@param/@var
docblocks when the native type is array/iterable/mixed/object/absent.
For a concrete wrapper class whose payload is expressed only via a
phpstan generic (PromiseInterface<Process>), the payload type was
silently dropped: Symfony\Component\Process\Process never appeared as
reached even though ProcessExecutor::executeAsync() hands one to
plugin callbacks. Treat known generic wrapper types the same as
array/iterable/mixed/object so their docblock payload is folded into
the closure.
Diffstat (limited to 'scripts/plugin-class-classifier/src/SourceParser.php')
| -rw-r--r-- | scripts/plugin-class-classifier/src/SourceParser.php | 22 |
1 files changed, 18 insertions, 4 deletions
diff --git a/scripts/plugin-class-classifier/src/SourceParser.php b/scripts/plugin-class-classifier/src/SourceParser.php index 591c54b1..bb293914 100644 --- a/scripts/plugin-class-classifier/src/SourceParser.php +++ b/scripts/plugin-class-classifier/src/SourceParser.php @@ -310,9 +310,20 @@ final class SourceParser } /** + * Native class types that carry a phpstan-only generic payload + * (`@phpstan-return PromiseInterface<Process>`). The native signature + * names the wrapper, not the payload, so the payload type is invisible + * unless the docblock is consulted too — same reason array/iterable/ + * mixed/object trigger docblock refinement below. + */ + private const GENERIC_WRAPPER_TYPES = [ + 'React\\Promise\\PromiseInterface', + ]; + + /** * Extracts class-like FQCNs from a native type node and reports whether - * the type invites docblock refinement (array/iterable/mixed/object or - * no type at all). + * the type invites docblock refinement (array/iterable/mixed/object, + * no type at all, or a generic wrapper type from GENERIC_WRAPPER_TYPES). * * @return array{0: list<string>, 1: bool} */ @@ -337,7 +348,7 @@ final class SourceParser $expandable = true; } } elseif ($t instanceof Name) { - $name = $t->toString(); + $name = ltrim($t->toString(), '\\'); $lower = strtolower($name); if ($lower === 'self' || $lower === 'static') { $classes[] = $currentClass; @@ -346,7 +357,10 @@ final class SourceParser $classes[] = $parentClass; } } else { - $classes[] = ltrim($name, '\\'); + $classes[] = $name; + if (in_array($name, self::GENERIC_WRAPPER_TYPES, true)) { + $expandable = true; + } } } }; |
