non-zero-intint<min, -1>|int<1, max>, a union with a hole punched at zero. An analyzer
that flattens the union back into a single range loses the hole and accepts
0, which is the one value the spelling exists to exclude. Analyzers that
model neither fall back to plain int and accept it too.
References:
- PHPStan TypeNodeResolver non-zero-int resolves to a union of IntegerRangeType(min, -1) and IntegerRangeType(1, max)| 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 | Enforced | phpdoc_advanced_fallback_non_zero_int.php:47: Argument 1 ($value) is 0 of type int (value: 0) but \Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt() takes non-zero-int (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_non_zero_int.php:35 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]
|
| phpstan | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Enforced reported Lv.5+ | phpdoc_advanced_fallback_non_zero_int.php:47: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt expects int<min, -1>|int<1, max>, 0 given. [identifier=argument.type] [reported-from-level=5] With strict-rulesphpdoc_advanced_fallback_non_zero_int.php:47: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt expects int<min, -1>|int<1, max>, 0 given. [identifier=argument.type]
|
| psalm | 6.16.1Psalm 6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac next: 7.0.0-beta19 |
Unrecognized (pzoom≠) | phpdoc_advanced_fallback_non_zero_int.php:23: Unrecognized type Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int in docblock for Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\returnsNonZeroInt [InvalidDocblock] phpdoc_advanced_fallback_non_zero_int.php:23: Method Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\returnsNonZeroInt does not have a return type, expecting -1|1 [MissingReturnType] phpdoc_advanced_fallback_non_zero_int.php:33: Unrecognized type Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int in docblock for Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt [InvalidDocblock] phpdoc_advanced_fallback_non_zero_int.php:35: Parameter $value has no provided type [MissingParamType] phpdoc_advanced_fallback_non_zero_int.php:40: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsInt cannot be mixed, expecting int [MixedArgument] pzoom (Psalm port)phpdoc_advanced_fallback_non_zero_int.php:23: Invalid return docblock type [InvalidDocblock] phpdoc_advanced_fallback_non_zero_int.php:23: Function Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\returnsNonZeroInt does not have a return type [MissingReturnType] phpdoc_advanced_fallback_non_zero_int.php:35: Invalid docblock type [InvalidDocblock] phpdoc_advanced_fallback_non_zero_int.php:35: Argument $value of Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt does not have a type [MissingParamType] phpdoc_advanced_fallback_non_zero_int.php:40: Argument 1 of acceptsInt cannot be mixed, expecting int [MixedArgument]
Expectation diffLine 40: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\acceptsInt cannot be mixed, expecting int [MixedArgument]"] |
| mago | 1.46.0mago 1.46.0 | Enforced | phpdoc_advanced_fallback_non_zero_int.php:47: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt`: expected `negative-int|positive-int`, but found `int(0)`. [invalid-argument]
|
| mir | 0.70.1mir 0.70.1 | Incidental (1/1) | phpdoc_advanced_fallback_non_zero_int.php:40: InvalidArgument: Argument $value of acceptsInt() expects 'int', got 'Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int' [MIR0201] phpdoc_advanced_fallback_non_zero_int.php:43: InvalidArgument: Argument $value of acceptsNonZeroInt() expects 'Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int', got '1' [MIR0201] phpdoc_advanced_fallback_non_zero_int.php:44: InvalidArgument: Argument $value of acceptsNonZeroInt() expects 'Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int', got '-1' [MIR0201] phpdoc_advanced_fallback_non_zero_int.php:47: InvalidArgument: Argument $value of acceptsNonZeroInt() expects 'Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int', got '0' [MIR0201]
Expectation diffLine 40: Expected valid value to be accepted, got ["InvalidArgument: Argument $value of acceptsInt() expects 'int', got 'Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\non-zero-int' [MIR0201]"] Line 43: Expected valid value to be accepted, got ["InvalidArgument: Argument $value of acceptsNonZeroInt() expects 'Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\non-zero-int', got '1' [MIR0201]"] Line 44: Expected valid value to be accepted, got ["InvalidArgument: Argument $value of acceptsNonZeroInt() expects 'Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\non-zero-int', got '-1' [MIR0201]"] |
| phpantom | 0.9.0phpantom_lsp 0.9.0 | Enforced | phpdoc_advanced_fallback_non_zero_int.php:47: Argument 1 ($value) expects non-zero-int, got 0 [type_mismatch_argument]
|
| intelephense | 1.18.5intelephense 1.18.5 | Widened to int | No diagnostics reported.
|
| phpy | 0.2.0phpy 0.2.0 | Unrecognized | phpdoc_advanced_fallback_non_zero_int.php:21: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int' phpdoc_advanced_fallback_non_zero_int.php:25: Return value of Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\returnsNonZeroInt() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int, int returned phpdoc_advanced_fallback_non_zero_int.php:33: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int' phpdoc_advanced_fallback_non_zero_int.php:40: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsInt() is expected to be of type int, Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int given phpdoc_advanced_fallback_non_zero_int.php:43: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int, int given phpdoc_advanced_fallback_non_zero_int.php:44: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int, int given phpdoc_advanced_fallback_non_zero_int.php:47: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int, int given
Expectation diffLine 25: Expected valid value to be accepted, got ["Return value of Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\returnsNonZeroInt() is expected to be of type Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\non-zero-int, int returned"] Line 40: Expected valid value to be accepted, got ["Argument '1' passed to Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\acceptsInt() is expected to be of type int, Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\non-zero-int given"] Line 43: Expected valid value to be accepted, got ["Argument '1' passed to Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\acceptsNonZeroInt() is expected to be of type Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\non-zero-int, int given"] Line 44: Expected valid value to be accepted, got ["Argument '1' passed to Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\acceptsNonZeroInt() is expected to be of type Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\non-zero-int, int given"] |
| qodana | 262.8665.325Qodana 262.8665.325 | Unrecognized | phpdoc_advanced_fallback_non_zero_int.php:21: Return type in PHPDoc does not match the actual return type [PhpReturnDocTypeMismatchInspection] phpdoc_advanced_fallback_non_zero_int.php:21: Undefined class 'non-zero-int' [PhpUndefinedClassInspection] phpdoc_advanced_fallback_non_zero_int.php:25: Return value type is not compatible with declared [PhpIncompatibleReturnTypeInspection] phpdoc_advanced_fallback_non_zero_int.php:25: Return value type is not compatible with declared [PhpIncompatibleReturnTypeInspection] phpdoc_advanced_fallback_non_zero_int.php:33: Undefined class 'non-zero-int' [PhpUndefinedClassInspection] phpdoc_advanced_fallback_non_zero_int.php:43: Parameter '1' type is not compatible with declaration [PhpParamsInspection] phpdoc_advanced_fallback_non_zero_int.php:44: Parameter '-1' type is not compatible with declaration [PhpParamsInspection] phpdoc_advanced_fallback_non_zero_int.php:47: Parameter '0' type is not compatible with declaration [PhpParamsInspection]
Expectation diffLine 25: Expected valid value to be accepted, got ["Return value type is not compatible with declared [PhpIncompatibleReturnTypeInspection]","Return value type is not compatible with declared [PhpIncompatibleReturnTypeInspection]"] Line 43: Expected valid value to be accepted, got ["Parameter '1' type is not compatible with declaration [PhpParamsInspection]"] Line 44: Expected valid value to be accepted, got ["Parameter '-1' type is not compatible with declaration [PhpParamsInspection]"] |
| 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_fallback_non_zero_int.php:12: Class or interface named \Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int does not exist [undefinedClass] phpdoc_advanced_fallback_non_zero_int.php:33: Class or interface named \Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int does not exist [undefinedClass]
Expectation diffLine 12: Unexpected errors ["Class or interface named \\Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\non-zero-int does not exist [undefinedClass]"] |
| steins | 0.1.5steins 0.1.5 (2026-08-12 revision 39b4cc1) | Enforced | phpdoc_advanced_fallback_non_zero_int.php:47: argument 0 to acceptsNonZeroInt() violates declared @param non-zero-int $value — declared contract violation [phpdoc.param-mismatch]
|
| phpstan-strict | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Enforced | phpdoc_advanced_fallback_non_zero_int.php:47: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt expects int<min, -1>|int<1, max>, 0 given. [identifier=argument.type]
|
| psalm-next | 7.0.0-beta19Psalm 7.0.0-beta19@7e751c06a756fa64dc4c759c09fe4a173afcb433 | Unrecognized | phpdoc_advanced_fallback_non_zero_int.php:23: Unrecognized type Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int in docblock for Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\returnsNonZeroInt [InvalidDocblock] phpdoc_advanced_fallback_non_zero_int.php:23: Method Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\returnsNonZeroInt does not have a return type, expecting -1|1 [MissingReturnType] phpdoc_advanced_fallback_non_zero_int.php:33: Unrecognized type Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\non-zero-int in docblock for Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsNonZeroInt [InvalidDocblock] phpdoc_advanced_fallback_non_zero_int.php:35: Parameter $value has no provided type [MissingParamType] phpdoc_advanced_fallback_non_zero_int.php:40: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt\acceptsInt cannot be mixed, expecting int [MixedArgument]
Expectation diffLine 40: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackNonZeroInt\\acceptsInt cannot be mixed, expecting int [MixedArgument]"] |
<?php
declare(strict_types=1);
namespace Conformance\Tests\PhpdocAdvancedFallbackNonZeroInt;
/**
* `non-zero-int`
*
* The only sign refinement that is not one interval: PHPStan resolves it to
* `int<min, -1>|int<1, max>`, a union with a hole punched at zero. An analyzer
* that flattens the union back into a single range loses the hole and accepts
* `0`, which is the one value the spelling exists to exclude. Analyzers that
* model neither fall back to plain `int` and accept it too.
*
* References:
* - PHPStan TypeNodeResolver `non-zero-int` resolves to a union of IntegerRangeType(min, -1) and IntegerRangeType(1, max)
*/
/**
* @return non-zero-int
*/
function returnsNonZeroInt() // T: non-zero-int
{
return \random_int(0, 1) === 1 ? 1 : -1; // V
}
function acceptsInt(int $value): void
{
}
/**
* @param non-zero-int $value
*/
function acceptsNonZeroInt($value): void // T: non-zero-int
{
}
// A `non-zero-int` value always satisfies a native `int` parameter.
acceptsInt(returnsNonZeroInt()); // V
// Both sides of the hole satisfy the parameter.
acceptsNonZeroInt(1); // V
acceptsNonZeroInt(-1); // V
// The hole itself.
acceptsNonZeroInt(0); // E?: 0 is not a non-zero-int