Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make key-of/value-of usable with non-const arrays #7396

Merged
merged 11 commits into from Jan 31, 2022
11 changes: 7 additions & 4 deletions UPGRADING.md
Expand Up @@ -42,7 +42,7 @@
- `Psalm\Type\Atomic\TIntRange`
- `Psalm\Type\Atomic\TIterable`
- `Psalm\Type\Atomic\TKeyedArray`
- `Psalm\Type\Atomic\TKeyOfClassConstant`
- `Psalm\Type\Atomic\TKeyOfArray`
- `Psalm\Type\Atomic\TList`
- `Psalm\Type\Atomic\TLiteralClassString`
- `Psalm\Type\Atomic\TLowercaseString`
Expand All @@ -64,7 +64,7 @@
- `Psalm\Type\Atomic\TTraitString`
- `Psalm\Type\Atomic\TTrue`
- `Psalm\Type\Atomic\TTypeAlias`
- `Psalm\Type\Atomic\TValueOfClassConstant`
- `Psalm\Type\Atomic\TValueOfArray`
- `Psalm\Type\Atomic\TVoid`
- `Psalm\Type\Union`

Expand Down Expand Up @@ -92,7 +92,7 @@
- `Psalm\Type\Atomic\TInt`
- `Psalm\Type\Atomic\TIterable`
- `Psalm\Type\Atomic\TKeyedArray`
- `Psalm\Type\Atomic\TKeyOfClassConstant`
- `Psalm\Type\Atomic\TKeyOfArray`
- `Psalm\Type\Atomic\TList`
- `Psalm\Type\Atomic\TLiteralClassString`
- `Psalm\Type\Atomic\TMixed`
Expand All @@ -109,7 +109,7 @@
- `Psalm\Type\Atomic\TTemplateParam`
- `Psalm\Type\Atomic\TTraitString`
- `Psalm\Type\Atomic\TTypeAlias`
- `Psalm\Type\Atomic\TValueOfClassConstant`
- `Psalm\Type\Atomic\TValueOfArray`
- `Psalm\Type\Atomic\TVoid`
- `Psalm\Type\Union`
- While not a BC break per se, all classes / interfaces / traits / enums under
Expand Down Expand Up @@ -154,6 +154,9 @@
- [BC] Atomic::getId() has now a first param $exact. Calling the method with false will return a less detailed version of the type in some cases (similarly to what __toString used to return)
- [BC] To remove a variable from the context, Context::remove(). Calling
`unset($context->vars_in_scope[$var_id])` can cause problems when using references.
- [BC] `TKeyOfClassConstant` has been renamed to `TKeyOfArray`.
- [BC] `TValueOfClassConstant` has been renamed to `TValueOfArray`.
- [BC] `TKeyOfTemplate` base class has been changed from `Scalar` to `Atomic`.

## Removed
- [BC] Property `Psalm\Codebase::$php_major_version` was removed, use
Expand Down
10 changes: 5 additions & 5 deletions docs/running_psalm/plugins/plugins_type_system.md
Expand Up @@ -49,13 +49,15 @@ The classes are as follows:

`TIntMaskOf` - as above, but used with with a reference to constants in code`int-mask<MyClass::CLASS_CONSTANT_*>` will corresponds to `1|2|3|4|5|6|7` if there are three constant 1, 2 and 4

`TKeyOfClassConstant` - Represents an offset of a class constant array.
`TKeyOfArray` - Represents an offset of an array (e.g. `key-of<MyClass::CLASS_CONSTANT>`).

`TValueOfClassConstant` - Represents a value of a class constant array.
`TValueOfArray` - Represents a value of an array (e.g. `value-of<MyClass::CLASS_CONSTANT>`).

`TTemplateIndexedAccess` - To be documented

`TTemplateKeyOf` - Represents the type used when using TKeyOfClassConstant when the type of the class constant array is a template
`TTemplateKeyOf` - Represents the type used when using TKeyOfArray when the type of the array is a template

`TTemplateValueOf` - Represents the type used when using TValueOfArray when the type of the array is a template

`TTypeAlias` - To be documented

Expand Down Expand Up @@ -277,5 +279,3 @@ Another way of creating these instances is to use the class `Psalm\Type` which i
```

You can find how Psalm would represent a given type as objects, by specifying the type as an input to this function, and calling `var_dump` on the result.


2 changes: 0 additions & 2 deletions src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php
Expand Up @@ -21,7 +21,6 @@
use Psalm\Internal\Provider\ReturnTypeProvider\ArraySliceReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\ArraySpliceReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\ArrayUniqueReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\ArrayValuesReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\ExplodeReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\FilterVarReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\FirstArgStringReturnTypeProvider;
Expand Down Expand Up @@ -79,7 +78,6 @@ public function __construct()
$this->registerClass(ArraySpliceReturnTypeProvider::class);
$this->registerClass(ArrayReverseReturnTypeProvider::class);
$this->registerClass(ArrayUniqueReturnTypeProvider::class);
$this->registerClass(ArrayValuesReturnTypeProvider::class);
orklah marked this conversation as resolved.
Show resolved Hide resolved
$this->registerClass(ArrayFillReturnTypeProvider::class);
$this->registerClass(FilterVarReturnTypeProvider::class);
$this->registerClass(IteratorToArrayReturnTypeProvider::class);
Expand Down

This file was deleted.

1 change: 0 additions & 1 deletion src/Psalm/Internal/Stubs/Generator/StubsGenerator.php
Expand Up @@ -7,7 +7,6 @@
use Psalm\Internal\Provider\FileStorageProvider;
use Psalm\Storage\FunctionLikeStorage;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TAssertionFalsy;
use Psalm\Type\Atomic\TEnumCase;
use Psalm\Type\Atomic\TFalse;
use Psalm\Type\Atomic\TIterable;
Expand Down
72 changes: 72 additions & 0 deletions src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php
Expand Up @@ -19,6 +19,7 @@
use Psalm\Type\Atomic\TEnumCase;
use Psalm\Type\Atomic\TGenericObject;
use Psalm\Type\Atomic\TIterable;
use Psalm\Type\Atomic\TKeyOfArray;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Atomic\TList;
use Psalm\Type\Atomic\TLiteralString;
Expand All @@ -32,7 +33,10 @@
use Psalm\Type\Atomic\TObjectWithProperties;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateKeyOf;
use Psalm\Type\Atomic\TTemplateParam;
use Psalm\Type\Atomic\TTemplateValueOf;
use Psalm\Type\Atomic\TValueOfArray;

use function array_merge;
use function array_values;
Expand Down Expand Up @@ -324,6 +328,74 @@ public static function isContainedBy(
return true;
}

if ($container_type_part instanceof TTemplateKeyOf) {
if (!$input_type_part instanceof TTemplateKeyOf) {
return false;
}

return UnionTypeComparator::isContainedBy(
$codebase,
$input_type_part->as,
$container_type_part->as
);
}

if ($input_type_part instanceof TTemplateKeyOf) {
$array_key_type = TKeyOfArray::getArrayKeyType($input_type_part->as);
if ($array_key_type === null) {
return false;
}

foreach ($array_key_type->getAtomicTypes() as $array_key_atomic) {
if (!self::isContainedBy(
$codebase,
$array_key_atomic,
$container_type_part,
$allow_interface_equality,
$allow_float_int_equality,
$atomic_comparison_result
)) {
return false;
}
}

return true;
}

if ($container_type_part instanceof TTemplateValueOf) {
if (!$input_type_part instanceof TTemplateValueOf) {
return false;
}

return UnionTypeComparator::isContainedBy(
$codebase,
$input_type_part->as,
$container_type_part->as
);
}

if ($input_type_part instanceof TTemplateValueOf) {
$array_value_type = TValueOfArray::getArrayValueType($input_type_part->as);
if ($array_value_type === null) {
return false;
}

foreach ($array_value_type->getAtomicTypes() as $array_value_atomic) {
if (!self::isContainedBy(
$codebase,
$array_value_atomic,
$container_type_part,
$allow_interface_equality,
$allow_float_int_equality,
$atomic_comparison_result
)) {
return false;
}
}

return true;
}

if ($container_type_part instanceof TTemplateParam && $input_type_part instanceof TTemplateParam) {
return UnionTypeComparator::isContainedBy(
$codebase,
Expand Down
27 changes: 1 addition & 26 deletions src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php
Expand Up @@ -5,7 +5,6 @@
use Psalm\Codebase;
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
use Psalm\Type\Atomic\Scalar;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TArrayKey;
use Psalm\Type\Atomic\TBool;
use Psalm\Type\Atomic\TCallableString;
Expand Down Expand Up @@ -35,7 +34,6 @@
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TSingleLetter;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateKeyOf;
use Psalm\Type\Atomic\TTemplateParam;
use Psalm\Type\Atomic\TTemplateParamClass;
use Psalm\Type\Atomic\TTraitString;
Expand Down Expand Up @@ -261,34 +259,11 @@ public static function isContainedBy(

if ($container_type_part instanceof TArrayKey
&& ($input_type_part instanceof TInt
|| $input_type_part instanceof TString
|| $input_type_part instanceof TTemplateKeyOf)
|| $input_type_part instanceof TString)
) {
return true;
}

if ($input_type_part instanceof TTemplateKeyOf) {
foreach ($input_type_part->as->getAtomicTypes() as $atomic_type) {
if ($atomic_type instanceof TArray) {
/** @var Scalar $array_key_atomic */
foreach ($atomic_type->type_params[0]->getAtomicTypes() as $array_key_atomic) {
if (!self::isContainedBy(
$codebase,
$array_key_atomic,
$container_type_part,
$allow_interface_equality,
$allow_float_int_equality,
$atomic_comparison_result
)) {
return false;
}
}
}
}

return true;
}

if ($input_type_part instanceof TArrayKey &&
($container_type_part instanceof TInt || $container_type_part instanceof TString)
) {
Expand Down