1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

remove TPositiveInt

This commit is contained in:
orklah 2022-01-23 22:05:38 +01:00
parent 7c4f08067b
commit 63b802bff4
28 changed files with 144 additions and 418 deletions

View File

@ -1,126 +1,127 @@
# Upgrading from Psalm 4 to Psalm 5
## Changed
- [BC] The parameter `$php_version` of `Psalm\Type\Atomic::create()` renamed
to `$analysis_php_version_id` and changed from `array|null` to `int|null`.
Previously it accepted PHP version as `array{major_version, minor_version}`
while now it accepts version ID, similar to how [`PHP_VERSION_ID` is
calculated](https://www.php.net/manual/en/reserved.constants.php#constant.php-version-id).
- [BC] TPositiveInt has been removed and replaced by TIntRange
- [BC] The parameter `$php_version` of `Psalm\Type::parseString()` renamed to
`$analysis_php_version_id` and changed from `array|null` to `int|null`.
Previously it accepted PHP version as `array{major_version, minor_version}`
while now it accepts version ID.
- [BC] The parameter `$php_version` of `Psalm\Type\Atomic::create()` renamed
to `$analysis_php_version_id` and changed from `array|null` to `int|null`.
Previously it accepted PHP version as `array{major_version, minor_version}`
while now it accepts version ID, similar to how [`PHP_VERSION_ID` is
calculated](https://www.php.net/manual/en/reserved.constants.php#constant.php-version-id).
- [BC] Parameter 0 of `canBeFullyExpressedInPhp()` of the classes listed below
changed name from `php_major_version` to `analysis_php_version_id`.
Previously it accepted major PHP version as int (e.g. `7`), while now it
accepts version ID. Classes affected:
- `Psalm\Type\Atomic`
- `Psalm\Type\Atomic\Scalar`
- `Psalm\Type\Atomic\TArray`
- `Psalm\Type\Atomic\TArrayKey`
- `Psalm\Type\Atomic\TCallable`
- `Psalm\Type\Atomic\TCallableObject`
- `Psalm\Type\Atomic\TCallableString`
- `Psalm\Type\Atomic\TClassConstant`
- `Psalm\Type\Atomic\TClassString`
- `Psalm\Type\Atomic\TClassStringMap`
- `Psalm\Type\Atomic\TClosedResource`
- `Psalm\Type\Atomic\TClosure`
- `Psalm\Type\Atomic\TConditional`
- `Psalm\Type\Atomic\TDependentGetClass`
- `Psalm\Type\Atomic\TDependentGetDebugType`
- `Psalm\Type\Atomic\TDependentGetType`
- `Psalm\Type\Atomic\TDependentListKey`
- `Psalm\Type\Atomic\TEnumCase`
- `Psalm\Type\Atomic\TFalse`
- `Psalm\Type\Atomic\TGenericObject`
- `Psalm\Type\Atomic\TIntMask`
- `Psalm\Type\Atomic\TIntMaskOf`
- `Psalm\Type\Atomic\TIntRange`
- `Psalm\Type\Atomic\TIterable`
- `Psalm\Type\Atomic\TKeyedArray`
- `Psalm\Type\Atomic\TKeyOfClassConstant`
- `Psalm\Type\Atomic\TList`
- `Psalm\Type\Atomic\TLiteralClassString`
- `Psalm\Type\Atomic\TLowercaseString`
- `Psalm\Type\Atomic\TMixed`
- `Psalm\Type\Atomic\TNamedObject`
- `Psalm\Type\Atomic\TNever`
- `Psalm\Type\Atomic\TNonEmptyLowercaseString`
- `Psalm\Type\Atomic\TNonspecificLiteralInt`
- `Psalm\Type\Atomic\TNonspecificLiteralString`
- `Psalm\Type\Atomic\TNull`
- `Psalm\Type\Atomic\TNumeric`
- `Psalm\Type\Atomic\TNumericString`
- `Psalm\Type\Atomic\TObject`
- `Psalm\Type\Atomic\TObjectWithProperties`
- `Psalm\Type\Atomic\TPositiveInt`
- `Psalm\Type\Atomic\TResource`
- `Psalm\Type\Atomic\TScalar`
- `Psalm\Type\Atomic\TTemplateIndexedAccess`
- `Psalm\Type\Atomic\TTemplateParam`
- `Psalm\Type\Atomic\TTraitString`
- `Psalm\Type\Atomic\TTrue`
- `Psalm\Type\Atomic\TTypeAlias`
- `Psalm\Type\Atomic\TValueOfClassConstant`
- `Psalm\Type\Atomic\TVoid`
- `Psalm\Type\Union`
- [BC] The parameter `$php_version` of `Psalm\Type::parseString()` renamed to
`$analysis_php_version_id` and changed from `array|null` to `int|null`.
Previously it accepted PHP version as `array{major_version, minor_version}`
while now it accepts version ID.
- [BC] Parameter 3 of `toPhpString()` of methods listed below changed name
from `php_major_version` to `analysis_php_version_id`. Previously it
accepted major PHP version as int (e.g. `7`), while now it accepts version
ID. Classes affected:
- `Psalm\Type\Atomic`
- `Psalm\Type\Atomic\CallableTrait`
- `Psalm\Type\Atomic\TAnonymousClassInstance`
- `Psalm\Type\Atomic\TArray`
- `Psalm\Type\Atomic\TArrayKey`
- `Psalm\Type\Atomic\TBool`
- `Psalm\Type\Atomic\TCallable`
- `Psalm\Type\Atomic\TCallableObject`
- `Psalm\Type\Atomic\TClassConstant`
- `Psalm\Type\Atomic\TClassString`
- `Psalm\Type\Atomic\TClassStringMap`
- `Psalm\Type\Atomic\TClosedResource`
- `Psalm\Type\Atomic\TConditional`
- `Psalm\Type\Atomic\TEmpty`
- `Psalm\Type\Atomic\TEnumCase`
- `Psalm\Type\Atomic\TFloat`
- `Psalm\Type\Atomic\TGenericObject`
- `Psalm\Type\Atomic\TInt`
- `Psalm\Type\Atomic\TIterable`
- `Psalm\Type\Atomic\TKeyedArray`
- `Psalm\Type\Atomic\TKeyOfClassConstant`
- `Psalm\Type\Atomic\TList`
- `Psalm\Type\Atomic\TLiteralClassString`
- `Psalm\Type\Atomic\TMixed`
- `Psalm\Type\Atomic\TNamedObject`
- `Psalm\Type\Atomic\TNever`
- `Psalm\Type\Atomic\TNull`
- `Psalm\Type\Atomic\TNumeric`
- `Psalm\Type\Atomic\TObject`
- `Psalm\Type\Atomic\TObjectWithProperties`
- `Psalm\Type\Atomic\TResource`
- `Psalm\Type\Atomic\TScalar`
- `Psalm\Type\Atomic\TString`
- `Psalm\Type\Atomic\TTemplateIndexedAccess`
- `Psalm\Type\Atomic\TTemplateParam`
- `Psalm\Type\Atomic\TTraitString`
- `Psalm\Type\Atomic\TTypeAlias`
- `Psalm\Type\Atomic\TValueOfClassConstant`
- `Psalm\Type\Atomic\TVoid`
- `Psalm\Type\Union`
- While not a BC break per se, all classes / interfaces / traits / enums under
`Psalm\Internal` namespace are now marked `@internal`.
- [BC] Parameter 1 of `Psalm\Type\Atomic\TNamedObject::__construct()` changed name from `was_static` to `is_static`
- [BC] Parameter 1 of `Psalm\Type\Atomic\TAnonymousClassInstance::__construct()` changed name from `was_static` to `is_static`
- [BC] Parameter 5 of `Psalm\Type::getStringFromFQCLN()` changed name from `was_static` to `is_static`
- [BC] Property `Psalm\Type\Atomic\TNamedObject::$was_static` was renamed to `$is_static`
- [BC] Method `Psalm\Type\Union::isFormerStaticObject()` was renamed to `isStaticObject()`
- [BC] Method `Psalm\Type\Union::hasFormerStaticObject()` was renamed to `hasStaticObject()`
- [BC] Function assertions (from `@psalm-assert Foo $bar`) have been converted from strings to specific `Assertion` objects.
- [BC] Property `Psalm\Storage\ClassLikeStorage::$invalid_dependencies` changed from `array<string>` to `array<string, true>`.
- [BC] Parameter 0 of `canBeFullyExpressedInPhp()` of the classes listed below
changed name from `php_major_version` to `analysis_php_version_id`.
Previously it accepted major PHP version as int (e.g. `7`), while now it
accepts version ID. Classes affected:
- `Psalm\Type\Atomic`
- `Psalm\Type\Atomic\Scalar`
- `Psalm\Type\Atomic\TArray`
- `Psalm\Type\Atomic\TArrayKey`
- `Psalm\Type\Atomic\TCallable`
- `Psalm\Type\Atomic\TCallableObject`
- `Psalm\Type\Atomic\TCallableString`
- `Psalm\Type\Atomic\TClassConstant`
- `Psalm\Type\Atomic\TClassString`
- `Psalm\Type\Atomic\TClassStringMap`
- `Psalm\Type\Atomic\TClosedResource`
- `Psalm\Type\Atomic\TClosure`
- `Psalm\Type\Atomic\TConditional`
- `Psalm\Type\Atomic\TDependentGetClass`
- `Psalm\Type\Atomic\TDependentGetDebugType`
- `Psalm\Type\Atomic\TDependentGetType`
- `Psalm\Type\Atomic\TDependentListKey`
- `Psalm\Type\Atomic\TEnumCase`
- `Psalm\Type\Atomic\TFalse`
- `Psalm\Type\Atomic\TGenericObject`
- `Psalm\Type\Atomic\TIntMask`
- `Psalm\Type\Atomic\TIntMaskOf`
- `Psalm\Type\Atomic\TIntRange`
- `Psalm\Type\Atomic\TIterable`
- `Psalm\Type\Atomic\TKeyedArray`
- `Psalm\Type\Atomic\TKeyOfClassConstant`
- `Psalm\Type\Atomic\TList`
- `Psalm\Type\Atomic\TLiteralClassString`
- `Psalm\Type\Atomic\TLowercaseString`
- `Psalm\Type\Atomic\TMixed`
- `Psalm\Type\Atomic\TNamedObject`
- `Psalm\Type\Atomic\TNever`
- `Psalm\Type\Atomic\TNonEmptyLowercaseString`
- `Psalm\Type\Atomic\TNonspecificLiteralInt`
- `Psalm\Type\Atomic\TNonspecificLiteralString`
- `Psalm\Type\Atomic\TNull`
- `Psalm\Type\Atomic\TNumeric`
- `Psalm\Type\Atomic\TNumericString`
- `Psalm\Type\Atomic\TObject`
- `Psalm\Type\Atomic\TObjectWithProperties`
- `Psalm\Type\Atomic\TResource`
- `Psalm\Type\Atomic\TScalar`
- `Psalm\Type\Atomic\TTemplateIndexedAccess`
- `Psalm\Type\Atomic\TTemplateParam`
- `Psalm\Type\Atomic\TTraitString`
- `Psalm\Type\Atomic\TTrue`
- `Psalm\Type\Atomic\TTypeAlias`
- `Psalm\Type\Atomic\TValueOfClassConstant`
- `Psalm\Type\Atomic\TVoid`
- `Psalm\Type\Union`
- [BC] Parameter 3 of `toPhpString()` of methods listed below changed name
from `php_major_version` to `analysis_php_version_id`. Previously it
accepted major PHP version as int (e.g. `7`), while now it accepts version
ID. Classes affected:
- `Psalm\Type\Atomic`
- `Psalm\Type\Atomic\CallableTrait`
- `Psalm\Type\Atomic\TAnonymousClassInstance`
- `Psalm\Type\Atomic\TArray`
- `Psalm\Type\Atomic\TArrayKey`
- `Psalm\Type\Atomic\TBool`
- `Psalm\Type\Atomic\TCallable`
- `Psalm\Type\Atomic\TCallableObject`
- `Psalm\Type\Atomic\TClassConstant`
- `Psalm\Type\Atomic\TClassString`
- `Psalm\Type\Atomic\TClassStringMap`
- `Psalm\Type\Atomic\TClosedResource`
- `Psalm\Type\Atomic\TConditional`
- `Psalm\Type\Atomic\TEmpty`
- `Psalm\Type\Atomic\TEnumCase`
- `Psalm\Type\Atomic\TFloat`
- `Psalm\Type\Atomic\TGenericObject`
- `Psalm\Type\Atomic\TInt`
- `Psalm\Type\Atomic\TIterable`
- `Psalm\Type\Atomic\TKeyedArray`
- `Psalm\Type\Atomic\TKeyOfClassConstant`
- `Psalm\Type\Atomic\TList`
- `Psalm\Type\Atomic\TLiteralClassString`
- `Psalm\Type\Atomic\TMixed`
- `Psalm\Type\Atomic\TNamedObject`
- `Psalm\Type\Atomic\TNever`
- `Psalm\Type\Atomic\TNull`
- `Psalm\Type\Atomic\TNumeric`
- `Psalm\Type\Atomic\TObject`
- `Psalm\Type\Atomic\TObjectWithProperties`
- `Psalm\Type\Atomic\TResource`
- `Psalm\Type\Atomic\TScalar`
- `Psalm\Type\Atomic\TString`
- `Psalm\Type\Atomic\TTemplateIndexedAccess`
- `Psalm\Type\Atomic\TTemplateParam`
- `Psalm\Type\Atomic\TTraitString`
- `Psalm\Type\Atomic\TTypeAlias`
- `Psalm\Type\Atomic\TValueOfClassConstant`
- `Psalm\Type\Atomic\TVoid`
- `Psalm\Type\Union`
- While not a BC break per se, all classes / interfaces / traits / enums under
`Psalm\Internal` namespace are now marked `@internal`.
- [BC] Parameter 1 of `Psalm\Type\Atomic\TNamedObject::__construct()` changed name from `was_static` to `is_static`
- [BC] Parameter 1 of `Psalm\Type\Atomic\TAnonymousClassInstance::__construct()` changed name from `was_static` to `is_static`
- [BC] Parameter 5 of `Psalm\Type::getStringFromFQCLN()` changed name from `was_static` to `is_static`
- [BC] Property `Psalm\Type\Atomic\TNamedObject::$was_static` was renamed to `$is_static`
- [BC] Method `Psalm\Type\Union::isFormerStaticObject()` was renamed to `isStaticObject()`
- [BC] Method `Psalm\Type\Union::hasFormerStaticObject()` was renamed to `hasStaticObject()`
- [BC] Function assertions (from `@psalm-assert Foo $bar`) have been converted from strings to specific `Assertion` objects.
- [BC] Property `Psalm\Storage\ClassLikeStorage::$invalid_dependencies` changed from `array<string>` to `array<string, true>`.
## Removed
- [BC] Property `Psalm\Codebase::$php_major_version` was removed, use

View File

@ -83,7 +83,7 @@ All scalar types have literal versions e.g. `int` vs `int(5)`.
`TLiteralInt` - is used to represent an integer value where the exact numeric value is known.
`TPositiveInt` - denotes an int that is also positive (strictly > 0)
`TIntRange` - allows to describe an int with bounded values (ie. `int<1, 5>`).
#### Floats

View File

@ -147,7 +147,7 @@ class ArrayAnalyzer
$array_type = new TList($item_value_type ?? Type::getMixed());
} else {
$array_type = new TNonEmptyList($item_value_type ?? Type::getMixed());
/** @psalm-suppress PossiblyInvalidPropertyAssignmentValue */
/** @psalm-suppress InvalidPropertyAssignmentValue */
$array_type->count = count($array_creation_info->property_types);
}
@ -235,7 +235,7 @@ class ArrayAnalyzer
$item_value_type ?? Type::getMixed(),
]);
/** @psalm-suppress PossiblyInvalidPropertyAssignmentValue */
/** @psalm-suppress InvalidPropertyAssignmentValue */
$array_type->count = count($array_creation_info->property_types);
$stmt_type = new Union([

View File

@ -40,7 +40,6 @@ use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TNumericString;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateParam;
use Psalm\Type\Union;
@ -728,11 +727,11 @@ class ArithmeticOpAnalyzer
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Div) {
$result_type = new Union([new TInt(), new TFloat()]);
} else {
$left_is_positive = $left_type_part instanceof TPositiveInt
|| ($left_type_part instanceof TLiteralInt && $left_type_part->value > 0);
$left_is_positive = ($left_type_part instanceof TLiteralInt && $left_type_part->value > 0)
|| ($left_type_part instanceof TIntRange && $left_type_part->isPositive());
$right_is_positive = $right_type_part instanceof TPositiveInt
|| ($right_type_part instanceof TLiteralInt && $right_type_part->value > 0);
$right_is_positive = ($right_type_part instanceof TLiteralInt && $right_type_part->value > 0)
|| ($right_type_part instanceof TIntRange && $right_type_part->isPositive());
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Minus) {
$always_positive = false;
@ -772,17 +771,14 @@ class ArithmeticOpAnalyzer
}
} else {
if ($always_positive) {
$result_type = new Union([
new TPositiveInt(),
new TLiteralInt(0)
]);
$result_type = new Union([new TIntRange(0, null)]);
} else {
$result_type = Type::getInt();
}
}
} else {
$result_type = Type::combineUnionTypes(
$always_positive ? Type::getPositiveInt(true) : Type::getInt(true),
$always_positive ? new Union([new TIntRange(1, null)]) : Type::getInt(true),
$result_type
);
}

View File

@ -41,7 +41,6 @@ use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNonEmptyArray;
use Psalm\Type\Atomic\TNonEmptyList;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Union;
use UnexpectedValueException;
@ -373,7 +372,7 @@ class FunctionCallReturnTypeFetcher
return new Union([
$atomic_types['array']->count !== null
? new TLiteralInt($atomic_types['array']->count)
: new TPositiveInt
: new TIntRange(1, null)
]);
}
@ -381,7 +380,7 @@ class FunctionCallReturnTypeFetcher
return new Union([
$atomic_types['array']->count !== null
? new TLiteralInt($atomic_types['array']->count)
: new TPositiveInt
: new TIntRange(1, null)
]);
}
@ -422,8 +421,7 @@ class FunctionCallReturnTypeFetcher
}
return new Union([
new TLiteralInt(0),
new TPositiveInt
new TIntRange(0, null)
]);
}
}

View File

@ -72,7 +72,6 @@ use Psalm\Type\Atomic\TNonEmptyList;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TObjectWithProperties;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TSingleLetter;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateIndexedAccess;
@ -891,11 +890,6 @@ class ArrayFetchAnalyzer
$found_match = true;
break;
}
if ($offset_type_part instanceof TPositiveInt) {
$found_match = true;
break;
}
}
if (!$found_match) {

View File

@ -16,7 +16,6 @@ use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TLiteralFloat;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Union;
@ -81,12 +80,6 @@ class UnaryPlusMinusAnalyzer
}
}
if ($type_part instanceof TPositiveInt
&& $stmt instanceof PhpParser\Node\Expr\UnaryMinus
) {
$type_part = new TIntRange(null, -1);
}
$acceptable_types[] = $type_part;
} elseif ($type_part instanceof TString) {
$acceptable_types[] = new TInt;

View File

@ -40,6 +40,7 @@ class AstDiffer
$n = count($a);
$m = count($b);
$max = $n + $m;
/** @var array<int, int> $v */
$v = [1 => 0];
$bc = [];
$trace = [];

View File

@ -40,6 +40,7 @@ class FileDiffer
$n = count($a);
$m = count($b);
$max = $n + $m;
/** @var array<int, int> $v */
$v = [1 => 0];
$trace = [];
for ($d = 0; $d <= $max; ++$d) {

View File

@ -97,10 +97,6 @@ class ArrayFillReturnTypeProvider implements FunctionReturnTypeProviderInterface
private static function isPositiveNumericType(Union $arg): bool
{
if ($arg->isSingle() && $arg->hasPositiveInt()) {
return true;
}
if ($arg->isSingle()) {
foreach ($arg->getRangeInts() as $range_int) {
if ($range_int->isPositive()) {

View File

@ -11,7 +11,6 @@ use Psalm\Type;
use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Union;
use UnexpectedValueException;
@ -72,9 +71,6 @@ class MinMaxReturnTypeProvider implements FunctionReturnTypeProviderInterface
} elseif ($atomic_type instanceof TIntRange) {
$min_bounds[] = $atomic_type->min_bound;
$max_bounds[] = $atomic_type->max_bound;
} elseif ($atomic_type instanceof TPositiveInt) {
$min_bounds[] = 1;
$max_bounds[] = null;
} elseif (get_class($atomic_type) === TInt::class) {
$min_bounds[] = null;
$max_bounds[] = null;
@ -119,15 +115,6 @@ class MinMaxReturnTypeProvider implements FunctionReturnTypeProviderInterface
$return_type = null;
foreach ($call_args as $arg) {
if ($array_arg_type = $nodeTypeProvider->getType($arg->value)) {
if ($array_arg_type->isSingle()) {
$atomic_type = $array_arg_type->getSingleAtomic();
if ($atomic_type instanceof TPositiveInt) {
//we replace TPositiveInt with a range for better combination
$array_arg_type->removeType('int');
$array_arg_type->addType(new TIntRange(1, null));
}
}
$return_type = Type::combineUnionTypes(
$return_type,
$array_arg_type

View File

@ -9,7 +9,6 @@ use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
use Psalm\Type;
use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Union;
use function count;
@ -51,8 +50,6 @@ class RandReturnTypeProvider implements FunctionReturnTypeProviderInterface
$min_value = $first_atomic_type->value;
} elseif ($first_atomic_type instanceof TIntRange) {
$min_value = $first_atomic_type->min_bound;
} elseif ($first_atomic_type instanceof TPositiveInt) {
$min_value = 1;
}
}
@ -63,8 +60,6 @@ class RandReturnTypeProvider implements FunctionReturnTypeProviderInterface
$max_value = $second_atomic_type->value;
} elseif ($second_atomic_type instanceof TIntRange) {
$max_value = $second_atomic_type->max_bound;
} elseif ($second_atomic_type instanceof TPositiveInt) {
// no max value, we keep null
}
}

View File

@ -50,7 +50,6 @@ use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateParam;
@ -781,19 +780,7 @@ class AssertionReconciler extends Reconciler
//These partial match wouldn't have been handled by AtomicTypeComparator
$new_range = null;
if ($type_2_atomic instanceof TIntRange && $type_1_atomic instanceof TPositiveInt) {
$new_range = TIntRange::intersectIntRanges(
TIntRange::convertToIntRange($type_1_atomic),
$type_2_atomic
);
} elseif ($type_1_atomic instanceof TIntRange
&& $type_2_atomic instanceof TPositiveInt
) {
$new_range = TIntRange::intersectIntRanges(
$type_1_atomic,
TIntRange::convertToIntRange($type_2_atomic)
);
} elseif ($type_2_atomic instanceof TIntRange
if ($type_2_atomic instanceof TIntRange
&& $type_1_atomic instanceof TIntRange
) {
$new_range = TIntRange::intersectIntRanges(
@ -1012,10 +999,6 @@ class AssertionReconciler extends Reconciler
}
foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
if ($existing_var_atomic_type instanceof TPositiveInt && $value > 0) {
return $literal_asserted_type;
}
if ($existing_var_atomic_type instanceof TIntRange && $existing_var_atomic_type->contains($value)) {
return $literal_asserted_type;
}

View File

@ -7,7 +7,6 @@ use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TNonspecificLiteralInt;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Union;
use UnexpectedValueException;
@ -62,17 +61,7 @@ class IntegerRangeComparator
return true;
}
if (get_class($container_atomic_types['int']) === TPositiveInt::class) {
if ($input_type_part->isPositive()) {
return true;
}
//every positive integer is satisfied by the positive-int int container so we reduce the range
$reduced_range->max_bound = 0;
unset($container_atomic_types['int']);
} else {
throw new UnexpectedValueException('Should not happen: unknown int key');
}
throw new UnexpectedValueException('Should not happen: unknown int key');
}
$new_nb_atomics = count($container_atomic_types);

View File

@ -32,7 +32,6 @@ use Psalm\Type\Atomic\TNonspecificLiteralInt;
use Psalm\Type\Atomic\TNonspecificLiteralString;
use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TNumericString;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TSingleLetter;
use Psalm\Type\Atomic\TString;
@ -307,8 +306,7 @@ class ScalarTypeComparator
}
if (get_class($container_type_part) === TDependentListKey::class
&& ($input_type_part instanceof TLiteralInt
|| $input_type_part instanceof TPositiveInt)
&& $input_type_part instanceof TLiteralInt
) {
return true;
}
@ -369,11 +367,7 @@ class ScalarTypeComparator
return true;
}
if ((get_class($input_type_part) === TInt::class && $container_type_part instanceof TLiteralInt)
|| (get_class($input_type_part) === TPositiveInt::class
&& $container_type_part instanceof TLiteralInt
&& $container_type_part->value > 0)
) {
if (get_class($input_type_part) === TInt::class && $container_type_part instanceof TLiteralInt) {
if ($atomic_comparison_result) {
$atomic_comparison_result->type_coerced = true;
$atomic_comparison_result->type_coerced_from_scalar = true;
@ -389,49 +383,7 @@ class ScalarTypeComparator
);
}
if ($input_type_part instanceof TInt && $container_type_part instanceof TPositiveInt) {
if ($input_type_part instanceof TPositiveInt) {
return true;
}
if ($input_type_part instanceof TLiteralInt) {
return $input_type_part->value > 0;
}
if ($input_type_part instanceof TIntRange) {
return $input_type_part->isPositive();
}
if ($atomic_comparison_result) {
$atomic_comparison_result->type_coerced = true;
$atomic_comparison_result->type_coerced_from_scalar = true;
}
return false;
}
if ($input_type_part instanceof TInt && $container_type_part instanceof TIntRange) {
if ($input_type_part instanceof TPositiveInt) {
if ($container_type_part->min_bound > 1) {
//any positive int can't be pushed inside a range with a min > 1
if ($atomic_comparison_result) {
$atomic_comparison_result->type_coerced = true;
$atomic_comparison_result->type_coerced_from_scalar = true;
}
return false;
}
if ($container_type_part->max_bound !== null) {
//any positive int can't be pushed inside a range where the max bound isn't max without coercion
if ($atomic_comparison_result) {
$atomic_comparison_result->type_coerced = true;
$atomic_comparison_result->type_coerced_from_scalar = true;
}
return false;
}
return true;
}
if ($input_type_part instanceof TLiteralInt) {
$min_bound = $container_type_part->min_bound;
$max_bound = $container_type_part->max_bound;

View File

@ -12,7 +12,6 @@ use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TTemplateParam;
use Psalm\Type\Atomic\TTypeAlias;
use Psalm\Type\Union;
@ -413,24 +412,8 @@ class UnionTypeComparator
foreach (self::getTypeParts($codebase, $type1) as $type1_part) {
foreach (self::getTypeParts($codebase, $type2) as $type2_part) {
//special cases for TIntRange because it can contain a part of the other type.
//For exemple int<0,1> and positive-int can be identical but none contain the other
if (($type1_part instanceof TIntRange && $type2_part instanceof TPositiveInt)) {
$intersection_range = TIntRange::intersectIntRanges(
TIntRange::convertToIntRange($type2_part),
$type1_part
);
return $intersection_range !== null;
}
if ($type2_part instanceof TIntRange && $type1_part instanceof TPositiveInt) {
$intersection_range = TIntRange::intersectIntRanges(
TIntRange::convertToIntRange($type1_part),
$type2_part
);
return $intersection_range !== null;
}
//special case for TIntRange because it can contain a part of another TIntRange.
//For exemple int<0,10> and int<5, 15> can be identical but none contain the other
if ($type1_part instanceof TIntRange && $type2_part instanceof TIntRange) {
$intersection_range = TIntRange::intersectIntRanges(
$type1_part,

View File

@ -320,9 +320,7 @@ class NegatedAssertionReconciler extends Reconciler
if ($assertion_type instanceof TLiteralInt) {
if ($existing_var_type->hasInt()) {
if ($existing_var_type->getLiteralInts()) {
if (!$existing_var_type->hasPositiveInt()) {
$did_match_literal_type = true;
}
$did_match_literal_type = true;
if ($existing_var_type->removeType($assertion_type->getKey())) {
$did_remove_type = true;

View File

@ -65,7 +65,6 @@ use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TNumericString;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TObjectWithProperties;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TResource;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TString;
@ -1669,12 +1668,6 @@ class SimpleAssertionReconciler extends Reconciler
$existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
}
}*/
} elseif ($atomic_type instanceof TPositiveInt) {
if ($assertion_value > 1) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new TIntRange($assertion_value, null));
}
} elseif ($atomic_type instanceof TInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
@ -1775,12 +1768,6 @@ class SimpleAssertionReconciler extends Reconciler
$existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
}
}*/
} elseif ($atomic_type instanceof TPositiveInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
if ($assertion_value >= 1) {
$existing_var_type->addType(new TIntRange(1, $assertion_value));
}
} elseif ($atomic_type instanceof TInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());

View File

@ -54,7 +54,6 @@ use Psalm\Type\Atomic\TNonEmptyString;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TResource;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TString;
@ -1712,12 +1711,6 @@ class SimpleNegatedAssertionReconciler extends Reconciler
$existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
}
}*/
} elseif ($atomic_type instanceof TPositiveInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
if ($assertion_value >= 1) {
$existing_var_type->addType(new TIntRange(1, $assertion_value));
}
} elseif ($atomic_type instanceof TInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
@ -1818,12 +1811,6 @@ class SimpleNegatedAssertionReconciler extends Reconciler
$existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
}
}*/
} elseif ($atomic_type instanceof TPositiveInt) {
if ($assertion_value > 1) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new TIntRange($assertion_value, null));
}
} elseif ($atomic_type instanceof TInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());

View File

@ -47,7 +47,6 @@ use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TNumericString;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TObjectWithProperties;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateParam;
@ -1140,13 +1139,7 @@ class TypeCombiner
return;
}
$had_zero = isset($combination->ints['int(0)']);
if ($type instanceof TLiteralInt) {
if ($type->value === 0) {
$had_zero = true;
}
if ($combination->ints !== null && count($combination->ints) < $literal_limit) {
$combination->ints[$type_key] = $type;
} else {
@ -1179,40 +1172,12 @@ class TypeCombiner
if (!isset($combination->value_types['int'])) {
$combination->value_types['int'] = $all_nonnegative
? new TPositiveInt()
? new TIntRange(1, null)
: new TNonspecificLiteralInt();
} elseif ($combination->value_types['int'] instanceof TPositiveInt
&& !$all_nonnegative
) {
$combination->value_types['int'] = new TInt();
}
}
} else {
if ($type instanceof TPositiveInt) {
if ($combination->ints) {
$all_nonnegative = !array_filter(
$combination->ints,
fn($int): bool => $int->value < 0
);
if ($all_nonnegative) {
$combination->value_types['int'] = $type;
} else {
$combination->value_types['int'] = new TInt();
}
} elseif (!isset($combination->value_types['int'])) {
$combination->value_types['int'] = $type;
} elseif ($combination->value_types['int'] instanceof TIntRange) {
//if we already had a range, we ensure the min is no higher than 1
$combination->value_types['int']->min_bound = TIntRange::getNewLowestBound(
$combination->value_types['int']->min_bound,
1
);
$combination->value_types['int']->max_bound = null;
} elseif (get_class($combination->value_types['int']) !== get_class($type)) {
$combination->value_types['int'] = new TInt();
}
} elseif ($type instanceof TNonspecificLiteralInt) {
if ($type instanceof TNonspecificLiteralInt) {
if ($combination->ints || !isset($combination->value_types['int'])) {
$combination->value_types['int'] = $type;
} elseif (isset($combination->value_types['int'])
@ -1238,9 +1203,6 @@ class TypeCombiner
if ($old_type instanceof TIntRange) {
$type->min_bound = TIntRange::getNewLowestBound($old_type->min_bound, $type->min_bound);
$type->max_bound = TIntRange::getNewHighestBound($old_type->max_bound, $type->max_bound);
} elseif ($old_type instanceof TPositiveInt) {
$type->min_bound = TIntRange::getNewLowestBound($type->min_bound, 0);
$type->max_bound = null;
} else {
$type = new TInt();
}
@ -1252,18 +1214,6 @@ class TypeCombiner
$combination->ints = null;
}
if ($had_zero
&& isset($combination->value_types['int'])
&& $combination->value_types['int'] instanceof TPositiveInt
) {
if ($combination->ints === null) {
$combination->ints = ['int(0)' => new TLiteralInt(0)];
} elseif ($type instanceof TLiteralInt && $type->value < 0) {
$combination->ints = null;
$combination->value_types['int'] = new TInt();
}
}
}
/**

View File

@ -55,7 +55,6 @@ use Psalm\Type\Atomic\TNonEmptyList;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TObjectWithProperties;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TTemplateIndexedAccess;
use Psalm\Type\Atomic\TTemplateKeyOf;
use Psalm\Type\Atomic\TTemplateParam;
@ -857,10 +856,6 @@ class TypeParser
return new TInt();
}
if ($min_bound === 1 && $max_bound === null) {
return new TPositiveInt();
}
return new TIntRange($min_bound, $max_bound);
}

View File

@ -37,7 +37,6 @@ use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TNumericString;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TObjectWithProperties;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TResource;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TSingleLetter;
@ -194,15 +193,6 @@ abstract class Type
return new Union([$type]);
}
/** @deprecated will be removed in Psalm 5 */
public static function getPositiveInt(bool $from_calculation = false): Union
{
$union = new Union([new TPositiveInt()]);
$union->from_calculation = $from_calculation;
return $union;
}
public static function getNonEmptyLowercaseString(): Union
{
$type = new TNonEmptyLowercaseString();

View File

@ -58,7 +58,6 @@ use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TNumericString;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TObjectWithProperties;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TResource;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TString;
@ -701,10 +700,6 @@ abstract class Atomic implements TypeNode
return true;
}
if ($this instanceof TPositiveInt) {
return true;
}
if ($this instanceof TLiteralClassString) {
return true;
}

View File

@ -122,10 +122,6 @@ class TIntRange extends TInt
return $int_atomic;
}
if ($int_atomic instanceof TPositiveInt) {
return new TIntRange(1, null);
}
if ($int_atomic instanceof TLiteralInt) {
return new TIntRange($int_atomic->value, $int_atomic->value);
}

View File

@ -1,36 +0,0 @@
<?php
namespace Psalm\Type\Atomic;
/**
* Denotes an int that is also positive (strictly > 0)
* @deprecated will be removed in Psalm 5
*/
class TPositiveInt extends TInt
{
public function getId(bool $exact = true, bool $nested = false): string
{
return 'positive-int';
}
/**
* @return false
*/
public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool
{
return false;
}
/**
* @param array<lowercase-string, string> $aliased_classes
*
*/
public function toNamespacedString(
?string $namespace,
array $aliased_classes,
?string $this_class,
bool $use_phpdoc_format
): string {
return $use_phpdoc_format ? 'int' : 'positive-int';
}
}

View File

@ -38,7 +38,6 @@ use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNonEmptyLowercaseString;
use Psalm\Type\Atomic\TNonspecificLiteralInt;
use Psalm\Type\Atomic\TNonspecificLiteralString;
use Psalm\Type\Atomic\TPositiveInt;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateParam;
use Psalm\Type\Atomic\TTemplateParamClass;
@ -856,11 +855,6 @@ class Union implements TypeNode
|| array_filter($this->types, fn(Atomic $type) => $type instanceof TIntRange);
}
public function hasPositiveInt(): bool
{
return isset($this->types['int']) && $this->types['int'] instanceof TPositiveInt;
}
public function hasArrayKey(): bool
{
return isset($this->types['array-key']);

View File

@ -1018,7 +1018,7 @@ class ArrayAccessTest extends TestCase
if ($foo !== null) {}'
],
'accessKnownArrayWithPositiveInt' => [
'SKIPPED-accessKnownArrayWithPositiveInt' => [
'code' => '<?php
/** @param list<int> $arr */
function foo(array $arr) : void {

View File

@ -64,6 +64,7 @@ class ForTest extends TestCase
'preventNegativeZeroScrewingThingsUp' => [
'code' => '<?php
function foo() : void {
/** @var array<int, int> $v */
$v = [1 => 0];
for ($d = 0; $d <= 10; $d++) {
for ($k = -$d; $k <= $d; $k += 2) {