Skip to content

Commit

Permalink
Merge pull request #7396 from Patrick-Remy/feat/key-of-value-of-impro…
Browse files Browse the repository at this point in the history
…vements

feat: make key-of/value-of usable with non-const arrays
  • Loading branch information
orklah committed Jan 31, 2022
2 parents fcfd4d3 + f480eb2 commit 2e01e9b
Show file tree
Hide file tree
Showing 25 changed files with 1,478 additions and 413 deletions.
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 @@ -155,6 +155,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);
$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

0 comments on commit 2e01e9b

Please sign in to comment.