class-string-map<T of X, T> is a Psalm-only dependent generic| Analyzer | Version | Result | Diagnostics |
|---|---|---|---|
| phan | 6.0.7Phan 6.0.7 php-ast version 1.1.3 PHP version used to run Phan: 8.5.9 | Unrecognized | phpdoc_advanced_psalm_class_string_map.php:19: Saw possibly unextractable annotation for a fragment of comment '* @param class-string-map<T of \Throwable, T> $map': after class-string, did not see an element name (will guess based on comment order) [PhanUnextractableAnnotationElementName] phpdoc_advanced_psalm_class_string_map.php:19: Saw a token Phan may have failed to parse after '* @param class-string-map<T of \Throwable, T> $map': after class-string, saw '-' [PhanUnextractableAnnotationSuffix]
|
| phpstan | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Unrecognized | phpdoc_advanced_psalm_class_string_map.php:19: PHPDoc tag @param has invalid value (class-string-map<T of \Throwable, T> $map): Unexpected token "of", expected '>' at offset 33 on line 2 [identifier=phpDoc.parseError] [reported-from-level=2] phpdoc_advanced_psalm_class_string_map.php:21: Function Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\acceptsThrowableMap() has parameter $map with no value type specified in iterable type array. [identifier=missingType.iterableValue] [reported-from-level=6] With strict-rulesphpdoc_advanced_psalm_class_string_map.php:19: PHPDoc tag @param has invalid value (class-string-map<T of \Throwable, T> $map): Unexpected token "of", expected '>' at offset 33 on line 2 [identifier=phpDoc.parseError] phpdoc_advanced_psalm_class_string_map.php:21: Function Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\acceptsThrowableMap() has parameter $map with no value type specified in iterable type array. [identifier=missingType.iterableValue]
|
| psalm | 6.16.1Psalm 6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac next: 7.0.0-beta19 |
Incidental (1/1) | phpdoc_advanced_psalm_class_string_map.php:26: Argument 1 of Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\acceptsThrowableMap expects class-string-map<T as object, T:class-string-map as object>, but array{RuntimeException::class: RuntimeException} provided [InvalidArgument]
phpdoc_advanced_psalm_class_string_map.php:29: Argument 1 of Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\acceptsThrowableMap expects class-string-map<T as object, T:class-string-map as object>, but array{RuntimeException::class: LogicException} provided [InvalidArgument]
Expectation diffLine 26: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPsalmClassStringMap\\acceptsThrowableMap expects class-string-map<T as object, T:class-string-map as object>, but array{RuntimeException::class: RuntimeException} provided [InvalidArgument]"] |
| mago | 1.46.0mago 1.46.0 | Unrecognized | phpdoc_advanced_psalm_class_string_map.php:19: Unexpected token in PHPDoc type [invalid-docblock] phpdoc_advanced_psalm_class_string_map.php:19: Failed to parse function-like docblock comment. [malformed-docblock-comment]
|
| mir | 0.70.1mir 0.70.1 | Incidental (1/1) | phpdoc_advanced_psalm_class_string_map.php:26: InvalidArgument: Argument $map of acceptsThrowableMap() expects 'array<class-string, Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\T>', got 'array<class-string<RuntimeException>, RuntimeException>' [MIR0201] phpdoc_advanced_psalm_class_string_map.php:29: InvalidArgument: Argument $map of acceptsThrowableMap() expects 'array<class-string, Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\T>', got 'array<class-string<RuntimeException>, LogicException>' [MIR0201]
Expectation diffLine 26: Expected valid value to be accepted, got ["InvalidArgument: Argument $map of acceptsThrowableMap() expects 'array<class-string, Conformance\\Tests\\PhpdocAdvancedPsalmClassStringMap\\T>', got 'array<class-string<RuntimeException>, RuntimeException>' [MIR0201]"] |
| phpantom | 0.9.0phpantom_lsp 0.9.0 | Unrecognized | phpdoc_advanced_psalm_class_string_map.php:19: Class 'Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\class-string-map<T of \Throwable, T>' not found [unknown_class]
|
| intelephense | 1.18.5intelephense 1.18.5 | Unrecognized | phpdoc_advanced_psalm_class_string_map.php:19: PHPDoc undefined type 'Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\T'. [P1133] phpdoc_advanced_psalm_class_string_map.php:19: PHPDoc parse error. Unexpected token. [P1129] phpdoc_advanced_psalm_class_string_map.php:19: PHPDoc undefined type 'Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\of'. [P1133]
|
| phpy | 0.2.0phpy 0.2.0 | Unrecognized | phpdoc_advanced_psalm_class_string_map.php:19: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\class-string-map'
|
| qodana | 262.8665.325Qodana 262.8665.325 | Unrecognized | phpdoc_advanced_psalm_class_string_map.php:19: Argument type does not match the declared [PhpDocSignatureInspection] phpdoc_advanced_psalm_class_string_map.php:19: Undefined class 'class-string-map' [PhpUndefinedClassInspection] phpdoc_advanced_psalm_class_string_map.php:19: Undefined class 'T' [PhpUndefinedClassInspection]
|
| noverify | 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 | Unrecognized ⚠ 1 false positive | phpdoc_advanced_psalm_class_string_map.php:12: Class or interface named \Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\class-string-map does not exist [undefinedClass] phpdoc_advanced_psalm_class_string_map.php:21: param $map miss matched with phpdoc type <<class-string-map<T of \Throwable, T>>> [funcParamTypeMissMatch]
Expectation diffLine 12: Unexpected errors ["Class or interface named \\Conformance\\Tests\\PhpdocAdvancedPsalmClassStringMap\\class-string-map does not exist [undefinedClass]"] |
| steins | 0.1.5steins 0.1.5 (2026-08-12 revision 39b4cc1) | Unrecognized | phpdoc_advanced_psalm_class_string_map.php:19: `@param class-string-map<T of \Throwable, T> $map` does not parse (expected CloseAngle, found Identifier) — the tag declares nothing [phpdoc.unparsable]
Notes: Psalm's `T of X` template bound isn't valid inside a plain generic-argument list in steins-phpdoc's grammar, so `class-string-map<T of \Throwable, T>` fails to parse entirely and the whole `@param` drops to no envelope (ADR-0029) — the dependent key-to-value relationship is never checked. |
| phpstan-strict | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Unrecognized | phpdoc_advanced_psalm_class_string_map.php:19: PHPDoc tag @param has invalid value (class-string-map<T of \Throwable, T> $map): Unexpected token "of", expected '>' at offset 33 on line 2 [identifier=phpDoc.parseError] phpdoc_advanced_psalm_class_string_map.php:21: Function Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\acceptsThrowableMap() has parameter $map with no value type specified in iterable type array. [identifier=missingType.iterableValue]
|
| psalm-next | 7.0.0-beta19Psalm 7.0.0-beta19@7e751c06a756fa64dc4c759c09fe4a173afcb433 | Incidental (1/1) | phpdoc_advanced_psalm_class_string_map.php:26: Argument 1 of Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\acceptsThrowableMap expects class-string-map<T as object, T:class-string-map as object>, but array{RuntimeException::class: RuntimeException} provided [InvalidArgument]
phpdoc_advanced_psalm_class_string_map.php:29: Argument 1 of Conformance\Tests\PhpdocAdvancedPsalmClassStringMap\acceptsThrowableMap expects class-string-map<T as object, T:class-string-map as object>, but array{RuntimeException::class: LogicException} provided [InvalidArgument]
Expectation diffLine 26: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPsalmClassStringMap\\acceptsThrowableMap expects class-string-map<T as object, T:class-string-map as object>, but array{RuntimeException::class: RuntimeException} provided [InvalidArgument]"] |
<?php
declare(strict_types=1);
namespace Conformance\Tests\PhpdocAdvancedPsalmClassStringMap;
/**
* `class-string-map<T of X, T>` is a Psalm-only dependent generic.
*
* It types a map whose keys are class-strings and whose value is an instance of
* that exact class. Only Psalm models this dependent relationship; other
* analyzers do not recognize the keyword. This records who parses it at all.
*
* References:
* - Psalm TClassStringMap (class-string-map<T as Foo, T>)
*/
/**
* @param class-string-map<T of \Throwable, T> $map
*/
function acceptsThrowableMap(array $map): void // T: class-string-map<T of \Throwable, T>
{
}
// A class-string key mapped to an instance of that class is valid.
acceptsThrowableMap([\RuntimeException::class => new \RuntimeException()]); // V
// A value that is not an instance of the key class is rejected by Psalm.
acceptsThrowableMap([\RuntimeException::class => new \LogicException()]); // E?: value must be an instance of the class-string key