@psalm-assert T $actual with a template taken from another argumentassertSame(1, $value), $value is int (the template pinned by
the first argument), so takesInt is valid and takesString is not.
Non-templated @psalm-assert is phpdoc_advanced_vendor_prefixed_assert_psalm.
References:
- Psalm adding_assertions.md: @psalm-assert
- vimeo/psalm 3.0.0 templated assertions| Analyzer | Version | Result | Diagnostics |
|---|---|---|---|
| phan | 6.0.11Phan 6.0.11 php-ast version 1.1.3 PHP version used to run Phan: 8.5.11 | Partly enforced (2/4) | assertions_templated_assert.php:25: Template type T not used in return value of function/method assertSame() [PhanTemplateTypeNotUsedInFunctionReturn]
|
| phpstan | 2.3.5PHPStan - PHP Static Analysis Tool 2.3.5 | Enforced reported Lv.4+ | assertions_templated_assert.php:41: Call to function is_string() with 1 will always evaluate to false. [reported-from-level=4] assertions_templated_assert.php:48: Call to function is_int() with 'x' will always evaluate to false. [reported-from-level=4] With strict-rulesassertions_templated_assert.php:41: Call to function is_string() with 1 will always evaluate to false. assertions_templated_assert.php:48: Call to function is_int() with 'x' will always evaluate to false.
|
| psalm | 6.20.9Psalm 6.20.9@f67170ac3c3b931f5088681e21d6c8babc601c08 next: 7.0.4 |
Enforced (pzoom≠) | assertions_templated_assert.php:41: Docblock-defined type 1 for $value is never string [DocblockTypeContradiction] assertions_templated_assert.php:41: Docblock-defined type 1 for $value is never string [RedundantConditionGivenDocblockType] assertions_templated_assert.php:48: Docblock-defined type 'x' for $value is never int [DocblockTypeContradiction] assertions_templated_assert.php:48: Docblock-defined type 'x' for $value is never int [RedundantConditionGivenDocblockType] pzoom (Psalm port)No diagnostics from pzoom.
|
| mago | 1.59.0mago 1.59.0 | Enforced | assertions_templated_assert.php:41: Impossible type assertion: `$value` of type `int(1)` can never be `string`. [impossible-type-comparison]
assertions_templated_assert.php:41: Redundant ternary operator: condition is always falsy. [impossible-condition]
assertions_templated_assert.php:48: Impossible type assertion: `$value` of type `string('x')` can never be `int`. [impossible-type-comparison]
assertions_templated_assert.php:48: Redundant ternary operator: condition is always falsy. [impossible-condition]
|
| mir | 0.91.0mir 0.91.0 | Partly enforced (2/4) | No diagnostics reported.
|
| phpantom | 0.11.1phpantom_lsp 0.11.1 | Not enforced | assertions_templated_assert.php:40: Argument 1 ($value) expects int, got object [type_mismatch_argument] assertions_templated_assert.php:47: Argument 1 ($value) expects string, got object [type_mismatch_argument]
Expectation diffLine 40: Expected silence (quiet probe), got ["Argument 1 ($value) expects int, got object [type_mismatch_argument]"] Line 47: Expected silence (quiet probe), got ["Argument 1 ($value) expects string, got object [type_mismatch_argument]"] |
| intelephense | 1.18.5intelephense 1.18.5 | Partly enforced (2/4) | No diagnostics reported.
|
| phpactor | 2026.10.07.0Phpactor 2026.10.07.0 | Partly enforced (2/4) | No diagnostics reported.
|
| phpy | 1.0.19490phpy 1.0.19490 | Partly enforced (2/4) | No diagnostics reported.
|
| qodana | 262.11879.104PhpStorm 262.11879.104 | Partly enforced (2/4) ⚠ 1 false positive | assertions_templated_assert.php:8: Undefined class 'T' [PhpUndefinedClassInspection]
Expectation diffLine 8: Unexpected errors ["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 | assertions_templated_assert.php:25: param $expected miss matched with phpdoc type <<T>> [funcParamTypeMissMatch] assertions_templated_assert.php:40: not null safety call in function Conformance\Tests\AssertionsTemplatedAssert\takesInt signature of param value [notNullSafetyFunctionArgumentVariable] assertions_templated_assert.php:47: not null safety call in function Conformance\Tests\AssertionsTemplatedAssert\takesString signature of param value [notNullSafetyFunctionArgumentVariable]
Expectation diffLine 40: Expected silence (quiet probe), got ["not null safety call in function Conformance\\Tests\\AssertionsTemplatedAssert\\takesInt signature of param value [notNullSafetyFunctionArgumentVariable]"] Line 47: Expected silence (quiet probe), got ["not null safety call in function Conformance\\Tests\\AssertionsTemplatedAssert\\takesString signature of param value [notNullSafetyFunctionArgumentVariable]"] |
| steins | 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) | Partly enforced (2/4) | No diagnostics reported.
|
| phpstan-strict | 2.3.5PHPStan - PHP Static Analysis Tool 2.3.5 | Enforced | assertions_templated_assert.php:41: Call to function is_string() with 1 will always evaluate to false. assertions_templated_assert.php:48: Call to function is_int() with 'x' will always evaluate to false.
|
| psalm-next | 7.0.4Psalm 7.0.4@fa5b1f6e665cb743838ffd865b8fc96207f9409f | Enforced | assertions_templated_assert.php:41: Docblock-defined type 1 for $value is never string [DocblockTypeContradiction] assertions_templated_assert.php:41: Docblock-defined type 1 for $value is never string [RedundantConditionGivenDocblockType] assertions_templated_assert.php:48: Docblock-defined type 'x' for $value is never int [DocblockTypeContradiction] assertions_templated_assert.php:48: Docblock-defined type 'x' for $value is never int [RedundantConditionGivenDocblockType]
|
<?php
declare(strict_types=1);
namespace Conformance\Tests\AssertionsTemplatedAssert;
/**
* `@psalm-assert T $actual` with a template taken from another argument.
*
* After `assertSame(1, $value)`, `$value` is `int` (the template pinned by
* the first argument), so `takesInt` is valid and `takesString` is not.
* Non-templated `@psalm-assert` is `phpdoc_advanced_vendor_prefixed_assert_psalm`.
*
* References:
* - Psalm adding_assertions.md: @psalm-assert
* - vimeo/psalm 3.0.0 templated assertions
*/
/**
* @template T
* @param T $expected
* @psalm-assert T $actual
* @phpstan-assert T $actual
*/
function assertSame(mixed $expected, mixed $actual): void // T: @psalm-assert
{
}
function takesInt(int $value): void
{
}
function takesString(string $value): void
{
}
function fromIntLiteral(mixed $value): void
{
assertSame(1, $value);
takesInt($value); // Q: the template assertion pins $value to int
echo \is_string($value) ? 'string' : 'int'; // E?: after assert int, is_string() is always false
}
function fromStringLiteral(mixed $value): void
{
assertSame('x', $value);
takesString($value); // Q: the template assertion pins $value to string
echo \is_int($value) ? 'int' : 'string'; // E?: after assert string, is_int() is always false
}