Collection<contravariant Dog> (call-site contravariance)contravariant to a type
argument lets a function accept Collection<Animal> (or Collection<mixed>)
where Collection<Dog> is written. Writes of Dog stay valid; reads are
mixed, because the item type is not bounded from above.
Declaration-site @template-contravariant is generics_template_contravariant.
References:
- PHPStan phpdoc-types: Generics (Collection<contravariant Type>)
- PHPStan "A guide to call-site generic variance"| 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_phpstan_call_site_contravariant.php:102: Saw possibly unextractable annotation for a fragment of comment '* @param Collection<contravariant Dog> $collection': after Collection, did not see an element name (will guess based on comment order) [PhanUnextractableAnnotationElementName] phpdoc_advanced_phpstan_call_site_contravariant.php:102: Saw a token Phan may have failed to parse after '* @param Collection<contravariant Dog> $collection': after Collection, saw '<' [PhanUnextractableAnnotationSuffix] phpdoc_advanced_phpstan_call_site_contravariant.php:109: Saw possibly unextractable annotation for a fragment of comment '* @param Collection<contravariant Dog> $collection': after Collection, did not see an element name (will guess based on comment order) [PhanUnextractableAnnotationElementName] phpdoc_advanced_phpstan_call_site_contravariant.php:109: Saw a token Phan may have failed to parse after '* @param Collection<contravariant Dog> $collection': after Collection, saw '<' [PhanUnextractableAnnotationSuffix] phpdoc_advanced_phpstan_call_site_contravariant.php:117: Saw possibly unextractable annotation for a fragment of comment '* @param Collection<contravariant Dog> $collection': after Collection, did not see an element name (will guess based on comment order) [PhanUnextractableAnnotationElementName] phpdoc_advanced_phpstan_call_site_contravariant.php:117: Saw a token Phan may have failed to parse after '* @param Collection<contravariant Dog> $collection': after Collection, saw '<' [PhanUnextractableAnnotationSuffix] phpdoc_advanced_phpstan_call_site_contravariant.php:148: Argument 1 ($collection) is animals() of type \Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection|\Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<\Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> but \Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesInvariant() takes \Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<\Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog> defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_phpstan_call_site_contravariant.php:127 [PhanTypeMismatchArgument]
|
| phpstan | 2.2.15PHPStan - PHP Static Analysis Tool 2.2.15 | Enforced reported Lv.5+ | phpdoc_advanced_phpstan_call_site_contravariant.php:121: Parameter #1 $dog of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesDog expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog, mixed given. [reported-from-level=9] phpdoc_advanced_phpstan_call_site_contravariant.php:142: Parameter #1 $collection of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<contravariant Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Cat> given. [reported-from-level=5] phpdoc_advanced_phpstan_call_site_contravariant.php:148: Parameter #1 $collection of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> given. [reported-from-level=5] With strict-rulesphpdoc_advanced_phpstan_call_site_contravariant.php:121: Parameter #1 $dog of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesDog expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog, mixed given. phpdoc_advanced_phpstan_call_site_contravariant.php:142: Parameter #1 $collection of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<contravariant Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Cat> given. phpdoc_advanced_phpstan_call_site_contravariant.php:148: Parameter #1 $collection of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> given.
|
| psalm | 6.18.0Psalm 6.18.0@536dd6236b39a5115385b0307b42d6d1ad431a02 next: 7.0.0-beta22 |
Partly enforced (1/2) | phpdoc_advanced_phpstan_call_site_contravariant.php:140: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> provided [ArgumentTypeCoercion] phpdoc_advanced_phpstan_call_site_contravariant.php:141: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<mixed> provided [MixedArgumentTypeCoercion] phpdoc_advanced_phpstan_call_site_contravariant.php:142: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Cat> provided [InvalidArgument] phpdoc_advanced_phpstan_call_site_contravariant.php:143: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\writeContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> provided [ArgumentTypeCoercion] phpdoc_advanced_phpstan_call_site_contravariant.php:148: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> provided [ArgumentTypeCoercion]
Expectation diffLine 140: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\takesContravariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Dog>, but parent type Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Animal> provided [ArgumentTypeCoercion]"] Line 141: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\takesContravariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Dog>, but parent type Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<mixed> provided [MixedArgumentTypeCoercion]"] Line 143: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\writeContravariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Dog>, but parent type Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Animal> provided [ArgumentTypeCoercion]"] Notes: Ignores the `contravariant` keyword and treats the parameter as invariant Collection<Dog>. Collection<Animal> and Collection<mixed> are rejected on the valid calls (`ArgumentTypeCoercion`); Cat is rejected for the invariant reason, not because it is not a supertype under a projection. |
| mago | 1.50.0mago 1.50.0 | Enforced | phpdoc_advanced_phpstan_call_site_contravariant.php:121: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesDog`: expected `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog`, but found `mixed`. [mixed-argument] phpdoc_advanced_phpstan_call_site_contravariant.php:142: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant`: expected `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>`, but found `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Cat>`. [invalid-argument] phpdoc_advanced_phpstan_call_site_contravariant.php:148: Argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesInvariant`: expected `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>`, but provided type `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal>` is less specific. [less-specific-argument]
|
| mir | 0.78.0mir 0.78.0 | Unrecognized | phpdoc_advanced_phpstan_call_site_contravariant.php:104: UndefinedDocblockClass: Docblock type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant Dog' does not exist [MIR1505] phpdoc_advanced_phpstan_call_site_contravariant.php:111: UndefinedDocblockClass: Docblock type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant Dog' does not exist [MIR1505] phpdoc_advanced_phpstan_call_site_contravariant.php:119: UndefinedDocblockClass: Docblock type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant Dog' does not exist [MIR1505] phpdoc_advanced_phpstan_call_site_contravariant.php:139: InvalidArgument: Argument $collection of takesContravariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant Dog>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>' [MIR0201] phpdoc_advanced_phpstan_call_site_contravariant.php:140: InvalidArgument: Argument $collection of takesContravariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant Dog>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal>' [MIR0201] phpdoc_advanced_phpstan_call_site_contravariant.php:142: InvalidArgument: Argument $collection of takesContravariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant Dog>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Cat>' [MIR0201] phpdoc_advanced_phpstan_call_site_contravariant.php:143: InvalidArgument: Argument $collection of writeContravariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant Dog>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal>' [MIR0201] phpdoc_advanced_phpstan_call_site_contravariant.php:144: InvalidArgument: Argument $collection of readContravariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant Dog>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>' [MIR0201] phpdoc_advanced_phpstan_call_site_contravariant.php:148: InvalidArgument: Argument $collection of takesInvariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal>' [MIR0201]
Expectation diffLine 139: Expected valid value to be accepted, got ["InvalidArgument: Argument $collection of takesContravariant() expects 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\contravariant Dog>', got 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Dog>' [MIR0201]"] Line 140: Expected valid value to be accepted, got ["InvalidArgument: Argument $collection of takesContravariant() expects 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\contravariant Dog>', got 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Animal>' [MIR0201]"] Line 143: Expected valid value to be accepted, got ["InvalidArgument: Argument $collection of writeContravariant() expects 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\contravariant Dog>', got 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Animal>' [MIR0201]"] Line 144: Expected valid value to be accepted, got ["InvalidArgument: Argument $collection of readContravariant() expects 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\contravariant Dog>', got 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Dog>' [MIR0201]"] Notes: Reads `contravariant Dog` as a single class name (with the space). Collection<Dog> itself fails to match Collection<contravariant Dog>. |
| phpantom | 0.10.0phpantom_lsp 0.10.0 | Partly enforced (1/2) | phpdoc_advanced_phpstan_call_site_contravariant.php:142: Argument 1 ($collection) expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, got Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Cat> [type_mismatch_argument]
Notes: Rejects Collection<Cat> (which invariant Collection<Dog> also does) but accepts Collection<Animal> and Collection<mixed>, and does not treat get() as mixed. Subtyping in the contravariant direction without the read restriction. |
| intelephense | 1.18.5intelephense 1.18.5 | Unrecognized | phpdoc_advanced_phpstan_call_site_contravariant.php:102: PHPDoc undefined type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant'. [P1133] phpdoc_advanced_phpstan_call_site_contravariant.php:109: PHPDoc undefined type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant'. [P1133] phpdoc_advanced_phpstan_call_site_contravariant.php:117: PHPDoc undefined type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\contravariant'. [P1133]
Notes: Reports an undefined type `contravariant` and does not apply call-site contravariance. |
| phpactor | 2026.06.23.0Phpactor 2026.06.23.0 | Not enforced | No diagnostics reported.
|
| phpy | 1.0.19274phpy 1.0.19274 | Not enforced | No diagnostics reported.
|
| qodana | 262.9437.196Qodana 262.9437.196 | Not enforced | No diagnostics reported.
Notes: Silent on Collection<contravariant Dog>: no undefined-type complaint on `contravariant`, and get() is not treated as mixed. |
| noverify | 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 | Unrecognized ⚠ 3 false positives | phpdoc_advanced_phpstan_call_site_contravariant.php:43: param $item miss matched with phpdoc type <<T>> [funcParamTypeMissMatch] phpdoc_advanced_phpstan_call_site_contravariant.php:104: param $collection miss matched with phpdoc type <<Collection<contravariant Dog>>> [funcParamTypeMissMatch] phpdoc_advanced_phpstan_call_site_contravariant.php:111: param $collection miss matched with phpdoc type <<Collection<contravariant Dog>>> [funcParamTypeMissMatch] phpdoc_advanced_phpstan_call_site_contravariant.php:119: param $collection miss matched with phpdoc type <<Collection<contravariant Dog>>> [funcParamTypeMissMatch] phpdoc_advanced_phpstan_call_site_contravariant.php:127: param $collection miss matched with phpdoc type <<Collection<Dog>>> [funcParamTypeMissMatch] phpdoc_advanced_phpstan_call_site_contravariant.php:134: param $collection miss matched with phpdoc type <<Collection<Dog>>> [funcParamTypeMissMatch]
Expectation diffLine 43: Unexpected errors ["param $item miss matched with phpdoc type <<T>> [funcParamTypeMissMatch]"] Line 127: Unexpected errors ["param $collection miss matched with phpdoc type <<Collection<Dog>>> [funcParamTypeMissMatch]"] Line 134: Unexpected errors ["param $collection miss matched with phpdoc type <<Collection<Dog>>> [funcParamTypeMissMatch]"] |
| steins | 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) | Not enforced | No diagnostics reported.
|
| phpstan-strict | 2.2.15PHPStan - PHP Static Analysis Tool 2.2.15 | Enforced | phpdoc_advanced_phpstan_call_site_contravariant.php:121: Parameter #1 $dog of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesDog expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog, mixed given. phpdoc_advanced_phpstan_call_site_contravariant.php:142: Parameter #1 $collection of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<contravariant Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Cat> given. phpdoc_advanced_phpstan_call_site_contravariant.php:148: Parameter #1 $collection of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> given.
|
| psalm-next | 7.0.0-beta22Psalm 7.0.0-beta22@de27e5724ee5997a1e3baee4d394ca6f4e46eba0 | Partly enforced (1/2) | phpdoc_advanced_phpstan_call_site_contravariant.php:140: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> provided [ArgumentTypeCoercion] phpdoc_advanced_phpstan_call_site_contravariant.php:141: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<mixed> provided [MixedArgumentTypeCoercion] phpdoc_advanced_phpstan_call_site_contravariant.php:142: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Cat> provided [InvalidArgument] phpdoc_advanced_phpstan_call_site_contravariant.php:143: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\writeContravariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> provided [ArgumentTypeCoercion] phpdoc_advanced_phpstan_call_site_contravariant.php:148: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Dog>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant\Animal> provided [ArgumentTypeCoercion]
Expectation diffLine 140: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\takesContravariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Dog>, but parent type Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Animal> provided [ArgumentTypeCoercion]"] Line 141: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\takesContravariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Dog>, but parent type Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<mixed> provided [MixedArgumentTypeCoercion]"] Line 143: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\writeContravariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Dog>, but parent type Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteContravariant\\Animal> provided [ArgumentTypeCoercion]"] Notes: Ignores the `contravariant` keyword and treats the parameter as invariant Collection<Dog>. Collection<Animal> and Collection<mixed> are rejected on the valid calls (`ArgumentTypeCoercion`); Cat is rejected for the invariant reason, not because it is not a supertype under a projection. |
<?php
declare(strict_types=1);
namespace Conformance\Tests\PhpdocAdvancedPhpstanCallSiteContravariant;
/**
* `Collection<contravariant Dog>` (call-site contravariance)
*
* A PHPStan call-site variance spelling. Attaching `contravariant` to a type
* argument lets a function accept `Collection<Animal>` (or `Collection<mixed>`)
* where `Collection<Dog>` is written. Writes of `Dog` stay valid; reads are
* `mixed`, because the item type is not bounded from above.
*
* Declaration-site `@template-contravariant` is `generics_template_contravariant`.
*
* References:
* - PHPStan phpdoc-types: Generics (`Collection<contravariant Type>`)
* - PHPStan "A guide to call-site generic variance"
*/
class Animal
{
}
class Cat extends Animal
{
}
class Dog extends Animal
{
}
/**
* @template T
*/
final class Collection
{
/**
* @param T $item
*/
public function __construct(
public mixed $item,
) {
}
/**
* @param T $item
*/
public function add($item): void
{
$this->item = $item;
}
/**
* @return T
*/
public function get(): mixed
{
return $this->item;
}
}
/**
* @return Collection<Dog>
*/
function dogs(): Collection
{
return new Collection(new Dog());
}
/**
* @return Collection<Animal>
*/
function animals(): Collection
{
return new Collection(new Animal());
}
/**
* @return Collection<Cat>
*/
function cats(): Collection
{
return new Collection(new Cat());
}
/**
* @param mixed $item
* @return Collection<mixed>
*/
function mixeds(mixed $item = null): Collection
{
return new Collection($item);
}
function takesDog(Dog $dog): void
{
}
/**
* @param Collection<contravariant Dog> $collection
*/
function takesContravariant(Collection $collection): void // T: Collection<contravariant Dog>
{
}
/**
* @param Collection<contravariant Dog> $collection
*/
function writeContravariant(Collection $collection): void // T: Collection<contravariant Dog>
{
$collection->add(new Dog()); // V: writes of Dog are allowed
}
/**
* @param Collection<contravariant Dog> $collection
*/
function readContravariant(Collection $collection): void // T: Collection<contravariant Dog>
{
takesDog($collection->get()); // E?: get() on a contravariant projection is mixed
}
/**
* @param Collection<Dog> $collection
*/
function takesInvariant(Collection $collection): void
{
}
/**
* @param Collection<Dog> $collection
*/
function readInvariant(Collection $collection): void
{
takesDog($collection->get()); // V: get() on Collection<Dog> is Dog
}
takesContravariant(dogs()); // V
takesContravariant(animals()); // V: Collection<Animal> satisfies Collection<contravariant Dog>
takesContravariant(mixeds()); // V: Collection<mixed> too
takesContravariant(cats()); // E?: Cat is not a supertype of Dog
writeContravariant(animals()); // V
readContravariant(dogs()); // V
readInvariant(dogs()); // V
// Contrast: without the keyword, Collection is invariant.
takesInvariant(animals()); // E?[noise]: Collection<Animal> is not Collection<Dog>