aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts/plugin-class-classifier
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-07-23 22:27:02 +0900
committernsfisis <nsfisis@gmail.com>2026-07-23 22:27:02 +0900
commit2d89dc761237b055ddb8c6dc817c46c0cd6dd44d (patch)
tree549ab0605564aeee40b499c0592a6a119e3877f7 /scripts/plugin-class-classifier
parentcec38036d6b3d5a4bd009f2eef3bd88791ab5828 (diff)
downloadphp-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')
-rw-r--r--scripts/plugin-class-classifier/src/SourceParser.php22
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;
+ }
}
}
};