mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Merge pull request #8283 from AndrolGenhald/feature/value-of-backed-enum
Allow `value-of` to work with backed enums (fixes #7874).
This commit is contained in:
commit
85fe7e8bcf
16
UPGRADING.md
16
UPGRADING.md
@ -42,7 +42,7 @@
|
|||||||
- `Psalm\Type\Atomic\TIntRange`
|
- `Psalm\Type\Atomic\TIntRange`
|
||||||
- `Psalm\Type\Atomic\TIterable`
|
- `Psalm\Type\Atomic\TIterable`
|
||||||
- `Psalm\Type\Atomic\TKeyedArray`
|
- `Psalm\Type\Atomic\TKeyedArray`
|
||||||
- `Psalm\Type\Atomic\TKeyOfArray`
|
- `Psalm\Type\Atomic\TKeyOf`
|
||||||
- `Psalm\Type\Atomic\TList`
|
- `Psalm\Type\Atomic\TList`
|
||||||
- `Psalm\Type\Atomic\TLiteralClassString`
|
- `Psalm\Type\Atomic\TLiteralClassString`
|
||||||
- `Psalm\Type\Atomic\TLowercaseString`
|
- `Psalm\Type\Atomic\TLowercaseString`
|
||||||
@ -64,7 +64,7 @@
|
|||||||
- `Psalm\Type\Atomic\TTraitString`
|
- `Psalm\Type\Atomic\TTraitString`
|
||||||
- `Psalm\Type\Atomic\TTrue`
|
- `Psalm\Type\Atomic\TTrue`
|
||||||
- `Psalm\Type\Atomic\TTypeAlias`
|
- `Psalm\Type\Atomic\TTypeAlias`
|
||||||
- `Psalm\Type\Atomic\TValueOfArray`
|
- `Psalm\Type\Atomic\TValueOf`
|
||||||
- `Psalm\Type\Atomic\TVoid`
|
- `Psalm\Type\Atomic\TVoid`
|
||||||
- `Psalm\Type\Union`
|
- `Psalm\Type\Union`
|
||||||
|
|
||||||
@ -92,7 +92,7 @@
|
|||||||
- `Psalm\Type\Atomic\TInt`
|
- `Psalm\Type\Atomic\TInt`
|
||||||
- `Psalm\Type\Atomic\TIterable`
|
- `Psalm\Type\Atomic\TIterable`
|
||||||
- `Psalm\Type\Atomic\TKeyedArray`
|
- `Psalm\Type\Atomic\TKeyedArray`
|
||||||
- `Psalm\Type\Atomic\TKeyOfArray`
|
- `Psalm\Type\Atomic\TKeyOf`
|
||||||
- `Psalm\Type\Atomic\TList`
|
- `Psalm\Type\Atomic\TList`
|
||||||
- `Psalm\Type\Atomic\TLiteralClassString`
|
- `Psalm\Type\Atomic\TLiteralClassString`
|
||||||
- `Psalm\Type\Atomic\TMixed`
|
- `Psalm\Type\Atomic\TMixed`
|
||||||
@ -109,7 +109,7 @@
|
|||||||
- `Psalm\Type\Atomic\TTemplateParam`
|
- `Psalm\Type\Atomic\TTemplateParam`
|
||||||
- `Psalm\Type\Atomic\TTraitString`
|
- `Psalm\Type\Atomic\TTraitString`
|
||||||
- `Psalm\Type\Atomic\TTypeAlias`
|
- `Psalm\Type\Atomic\TTypeAlias`
|
||||||
- `Psalm\Type\Atomic\TValueOfArray`
|
- `Psalm\Type\Atomic\TValueOf`
|
||||||
- `Psalm\Type\Atomic\TVoid`
|
- `Psalm\Type\Atomic\TVoid`
|
||||||
- `Psalm\Type\Union`
|
- `Psalm\Type\Union`
|
||||||
- While not a BC break per se, all classes / interfaces / traits / enums under
|
- While not a BC break per se, all classes / interfaces / traits / enums under
|
||||||
@ -155,8 +155,8 @@
|
|||||||
- [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] 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, use `Context::remove()`. Calling
|
- [BC] To remove a variable from the context, use `Context::remove()`. Calling
|
||||||
`unset($context->vars_in_scope[$var_id])` can cause problems when using references.
|
`unset($context->vars_in_scope[$var_id])` can cause problems when using references.
|
||||||
- [BC] `TKeyOfClassConstant` has been renamed to `TKeyOfArray`.
|
- [BC] `TKeyOfClassConstant` has been renamed to `TKeyOf`.
|
||||||
- [BC] `TValueOfClassConstant` has been renamed to `TValueOfArray`.
|
- [BC] `TValueOfClassConstant` has been renamed to `TValueOf`.
|
||||||
- [BC] `TKeyOfTemplate` base class has been changed from `Scalar` to `Atomic`.
|
- [BC] `TKeyOfTemplate` base class has been changed from `Scalar` to `Atomic`.
|
||||||
- [BC] Class `Psalm\FileManipulation` became final
|
- [BC] Class `Psalm\FileManipulation` became final
|
||||||
- [BC] Class `Psalm\Context` became final
|
- [BC] Class `Psalm\Context` became final
|
||||||
@ -742,13 +742,13 @@
|
|||||||
- [BC] Class `Psalm\Type\Atomic\TLiteralInt` became final
|
- [BC] Class `Psalm\Type\Atomic\TLiteralInt` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TTrue` became final
|
- [BC] Class `Psalm\Type\Atomic\TTrue` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TDependentGetClass` became final
|
- [BC] Class `Psalm\Type\Atomic\TDependentGetClass` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TValueOfArray` became final
|
- [BC] Class `Psalm\Type\Atomic\TValueOf` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TGenericObject` became final
|
- [BC] Class `Psalm\Type\Atomic\TGenericObject` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TNonEmptyLowercaseString` became final
|
- [BC] Class `Psalm\Type\Atomic\TNonEmptyLowercaseString` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TEnumCase` became final
|
- [BC] Class `Psalm\Type\Atomic\TEnumCase` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TCallableKeyedArray` became final
|
- [BC] Class `Psalm\Type\Atomic\TCallableKeyedArray` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TDependentGetDebugType` became final
|
- [BC] Class `Psalm\Type\Atomic\TDependentGetDebugType` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TKeyOfArray` became final
|
- [BC] Class `Psalm\Type\Atomic\TKeyOf` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TNonspecificLiteralInt` became final
|
- [BC] Class `Psalm\Type\Atomic\TNonspecificLiteralInt` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TObjectWithProperties` became final
|
- [BC] Class `Psalm\Type\Atomic\TObjectWithProperties` became final
|
||||||
- [BC] Class `Psalm\Type\Atomic\TTemplateValueOf` became final
|
- [BC] Class `Psalm\Type\Atomic\TTemplateValueOf` became final
|
||||||
|
@ -49,15 +49,15 @@ The classes are as follows:
|
|||||||
|
|
||||||
`TIntMaskOf` - as above, but used with a reference to constants in code `int-mask-of<MyClass::CLASS_CONSTANT_*>` will corresponds to `1|2|3|4|5|6|7` if there are three constant 1, 2 and 4
|
`TIntMaskOf` - as above, but used with a reference to constants in code `int-mask-of<MyClass::CLASS_CONSTANT_*>` will corresponds to `1|2|3|4|5|6|7` if there are three constant 1, 2 and 4
|
||||||
|
|
||||||
`TKeyOfArray` - Represents an offset of an array (e.g. `key-of<MyClass::CLASS_CONSTANT>`).
|
`TKeyOf` - Represents an offset of an array (e.g. `key-of<MyClass::CLASS_CONSTANT>`).
|
||||||
|
|
||||||
`TValueOfArray` - Represents a value of an array (e.g. `value-of<MyClass::CLASS_CONSTANT>`).
|
`TValueOf` - Represents a value of an array or enum (e.g. `value-of<MyClass::CLASS_CONSTANT>`).
|
||||||
|
|
||||||
`TTemplateIndexedAccess` - To be documented
|
`TTemplateIndexedAccess` - To be documented
|
||||||
|
|
||||||
`TTemplateKeyOf` - Represents the type used when using TKeyOfArray when the type of the array is a template
|
`TTemplateKeyOf` - Represents the type used when using TKeyOf 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
|
`TTemplateValueOf` - Represents the type used when using TValueOf when the type of the array or enum is a template
|
||||||
|
|
||||||
`TPropertiesOf` - Represents properties and their types of a class as a keyed array (e.g. `properties-of<MyClass>`)
|
`TPropertiesOf` - Represents properties and their types of a class as a keyed array (e.g. `properties-of<MyClass>`)
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ use Psalm\Type\Atomic\TEmptyMixed;
|
|||||||
use Psalm\Type\Atomic\TEnumCase;
|
use Psalm\Type\Atomic\TEnumCase;
|
||||||
use Psalm\Type\Atomic\TGenericObject;
|
use Psalm\Type\Atomic\TGenericObject;
|
||||||
use Psalm\Type\Atomic\TIterable;
|
use Psalm\Type\Atomic\TIterable;
|
||||||
use Psalm\Type\Atomic\TKeyOfArray;
|
use Psalm\Type\Atomic\TKeyOf;
|
||||||
use Psalm\Type\Atomic\TKeyedArray;
|
use Psalm\Type\Atomic\TKeyedArray;
|
||||||
use Psalm\Type\Atomic\TList;
|
use Psalm\Type\Atomic\TList;
|
||||||
use Psalm\Type\Atomic\TLiteralString;
|
use Psalm\Type\Atomic\TLiteralString;
|
||||||
@ -36,7 +36,7 @@ use Psalm\Type\Atomic\TString;
|
|||||||
use Psalm\Type\Atomic\TTemplateKeyOf;
|
use Psalm\Type\Atomic\TTemplateKeyOf;
|
||||||
use Psalm\Type\Atomic\TTemplateParam;
|
use Psalm\Type\Atomic\TTemplateParam;
|
||||||
use Psalm\Type\Atomic\TTemplateValueOf;
|
use Psalm\Type\Atomic\TTemplateValueOf;
|
||||||
use Psalm\Type\Atomic\TValueOfArray;
|
use Psalm\Type\Atomic\TValueOf;
|
||||||
|
|
||||||
use function array_merge;
|
use function array_merge;
|
||||||
use function array_values;
|
use function array_values;
|
||||||
@ -341,7 +341,7 @@ class AtomicTypeComparator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($input_type_part instanceof TTemplateKeyOf) {
|
if ($input_type_part instanceof TTemplateKeyOf) {
|
||||||
$array_key_type = TKeyOfArray::getArrayKeyType($input_type_part->as);
|
$array_key_type = TKeyOf::getArrayKeyType($input_type_part->as);
|
||||||
if ($array_key_type === null) {
|
if ($array_key_type === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -375,7 +375,7 @@ class AtomicTypeComparator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($input_type_part instanceof TTemplateValueOf) {
|
if ($input_type_part instanceof TTemplateValueOf) {
|
||||||
$array_value_type = TValueOfArray::getArrayValueType($input_type_part->as);
|
$array_value_type = TValueOf::getValueType($input_type_part->as, $codebase);
|
||||||
if ($array_value_type === null) {
|
if ($array_value_type === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use Psalm\Type\Atomic\TClassString;
|
|||||||
use Psalm\Type\Atomic\TConditional;
|
use Psalm\Type\Atomic\TConditional;
|
||||||
use Psalm\Type\Atomic\TInt;
|
use Psalm\Type\Atomic\TInt;
|
||||||
use Psalm\Type\Atomic\TIterable;
|
use Psalm\Type\Atomic\TIterable;
|
||||||
use Psalm\Type\Atomic\TKeyOfArray;
|
use Psalm\Type\Atomic\TKeyOf;
|
||||||
use Psalm\Type\Atomic\TKeyedArray;
|
use Psalm\Type\Atomic\TKeyedArray;
|
||||||
use Psalm\Type\Atomic\TLiteralInt;
|
use Psalm\Type\Atomic\TLiteralInt;
|
||||||
use Psalm\Type\Atomic\TLiteralString;
|
use Psalm\Type\Atomic\TLiteralString;
|
||||||
@ -26,7 +26,7 @@ use Psalm\Type\Atomic\TTemplateParam;
|
|||||||
use Psalm\Type\Atomic\TTemplateParamClass;
|
use Psalm\Type\Atomic\TTemplateParamClass;
|
||||||
use Psalm\Type\Atomic\TTemplatePropertiesOf;
|
use Psalm\Type\Atomic\TTemplatePropertiesOf;
|
||||||
use Psalm\Type\Atomic\TTemplateValueOf;
|
use Psalm\Type\Atomic\TTemplateValueOf;
|
||||||
use Psalm\Type\Atomic\TValueOfArray;
|
use Psalm\Type\Atomic\TValueOf;
|
||||||
use Psalm\Type\Union;
|
use Psalm\Type\Union;
|
||||||
use UnexpectedValueException;
|
use UnexpectedValueException;
|
||||||
|
|
||||||
@ -345,15 +345,15 @@ class TemplateInferredTypeReplacer
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ($atomic_type instanceof TTemplateKeyOf
|
if ($atomic_type instanceof TTemplateKeyOf
|
||||||
&& TKeyOfArray::isViableTemplateType($template_type)
|
&& TKeyOf::isViableTemplateType($template_type)
|
||||||
) {
|
) {
|
||||||
return new TKeyOfArray(clone $template_type);
|
return new TKeyOf(clone $template_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($atomic_type instanceof TTemplateValueOf
|
if ($atomic_type instanceof TTemplateValueOf
|
||||||
&& TValueOfArray::isViableTemplateType($template_type)
|
&& TValueOf::isViableTemplateType($template_type)
|
||||||
) {
|
) {
|
||||||
return new TValueOfArray(clone $template_type);
|
return new TValueOf(clone $template_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -22,7 +22,7 @@ use Psalm\Type\Atomic\TInt;
|
|||||||
use Psalm\Type\Atomic\TIntMask;
|
use Psalm\Type\Atomic\TIntMask;
|
||||||
use Psalm\Type\Atomic\TIntMaskOf;
|
use Psalm\Type\Atomic\TIntMaskOf;
|
||||||
use Psalm\Type\Atomic\TIterable;
|
use Psalm\Type\Atomic\TIterable;
|
||||||
use Psalm\Type\Atomic\TKeyOfArray;
|
use Psalm\Type\Atomic\TKeyOf;
|
||||||
use Psalm\Type\Atomic\TKeyedArray;
|
use Psalm\Type\Atomic\TKeyedArray;
|
||||||
use Psalm\Type\Atomic\TList;
|
use Psalm\Type\Atomic\TList;
|
||||||
use Psalm\Type\Atomic\TLiteralClassString;
|
use Psalm\Type\Atomic\TLiteralClassString;
|
||||||
@ -34,7 +34,7 @@ use Psalm\Type\Atomic\TObjectWithProperties;
|
|||||||
use Psalm\Type\Atomic\TPropertiesOf;
|
use Psalm\Type\Atomic\TPropertiesOf;
|
||||||
use Psalm\Type\Atomic\TTemplateParam;
|
use Psalm\Type\Atomic\TTemplateParam;
|
||||||
use Psalm\Type\Atomic\TTypeAlias;
|
use Psalm\Type\Atomic\TTypeAlias;
|
||||||
use Psalm\Type\Atomic\TValueOfArray;
|
use Psalm\Type\Atomic\TValueOf;
|
||||||
use Psalm\Type\Atomic\TVoid;
|
use Psalm\Type\Atomic\TVoid;
|
||||||
use Psalm\Type\Union;
|
use Psalm\Type\Union;
|
||||||
use ReflectionProperty;
|
use ReflectionProperty;
|
||||||
@ -363,10 +363,10 @@ class TypeExpander
|
|||||||
return [$return_type];
|
return [$return_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($return_type instanceof TKeyOfArray
|
if ($return_type instanceof TKeyOf
|
||||||
|| $return_type instanceof TValueOfArray
|
|| $return_type instanceof TValueOf
|
||||||
) {
|
) {
|
||||||
return self::expandKeyOfValueOfArray(
|
return self::expandKeyOfValueOf(
|
||||||
$codebase,
|
$codebase,
|
||||||
$return_type,
|
$return_type,
|
||||||
$self_class,
|
$self_class,
|
||||||
@ -965,11 +965,11 @@ class TypeExpander
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TKeyOfArray|TValueOfArray $return_type
|
* @param TKeyOf|TValueOf $return_type
|
||||||
* @param string|TNamedObject|TTemplateParam|null $static_class_type
|
* @param string|TNamedObject|TTemplateParam|null $static_class_type
|
||||||
* @return non-empty-list<Atomic>
|
* @return non-empty-list<Atomic>
|
||||||
*/
|
*/
|
||||||
private static function expandKeyOfValueOfArray(
|
private static function expandKeyOfValueOf(
|
||||||
Codebase $codebase,
|
Codebase $codebase,
|
||||||
Atomic &$return_type,
|
Atomic &$return_type,
|
||||||
?string $self_class,
|
?string $self_class,
|
||||||
@ -1025,12 +1025,12 @@ class TypeExpander
|
|||||||
|
|
||||||
if (!$constant_type
|
if (!$constant_type
|
||||||
|| (
|
|| (
|
||||||
$return_type instanceof TKeyOfArray
|
$return_type instanceof TKeyOf
|
||||||
&& !TKeyOfArray::isViableTemplateType($constant_type)
|
&& !TKeyOf::isViableTemplateType($constant_type)
|
||||||
)
|
)
|
||||||
|| (
|
|| (
|
||||||
$return_type instanceof TValueOfArray
|
$return_type instanceof TValueOf
|
||||||
&& !TValueOfArray::isViableTemplateType($constant_type)
|
&& !TValueOf::isViableTemplateType($constant_type)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if ($throw_on_unresolvable_constant) {
|
if ($throw_on_unresolvable_constant) {
|
||||||
@ -1049,10 +1049,10 @@ class TypeExpander
|
|||||||
return [$return_type];
|
return [$return_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($return_type instanceof TKeyOfArray) {
|
if ($return_type instanceof TKeyOf) {
|
||||||
$new_return_types = TKeyOfArray::getArrayKeyType(new Union($type_atomics));
|
$new_return_types = TKeyOf::getArrayKeyType(new Union($type_atomics));
|
||||||
} else {
|
} else {
|
||||||
$new_return_types = TValueOfArray::getArrayValueType(new Union($type_atomics));
|
$new_return_types = TValueOf::getValueType(new Union($type_atomics), $codebase);
|
||||||
}
|
}
|
||||||
if ($new_return_types === null) {
|
if ($new_return_types === null) {
|
||||||
return [$return_type];
|
return [$return_type];
|
||||||
|
@ -41,7 +41,7 @@ use Psalm\Type\Atomic\TIntMask;
|
|||||||
use Psalm\Type\Atomic\TIntMaskOf;
|
use Psalm\Type\Atomic\TIntMaskOf;
|
||||||
use Psalm\Type\Atomic\TIntRange;
|
use Psalm\Type\Atomic\TIntRange;
|
||||||
use Psalm\Type\Atomic\TIterable;
|
use Psalm\Type\Atomic\TIterable;
|
||||||
use Psalm\Type\Atomic\TKeyOfArray;
|
use Psalm\Type\Atomic\TKeyOf;
|
||||||
use Psalm\Type\Atomic\TKeyedArray;
|
use Psalm\Type\Atomic\TKeyedArray;
|
||||||
use Psalm\Type\Atomic\TList;
|
use Psalm\Type\Atomic\TList;
|
||||||
use Psalm\Type\Atomic\TLiteralClassString;
|
use Psalm\Type\Atomic\TLiteralClassString;
|
||||||
@ -63,7 +63,7 @@ use Psalm\Type\Atomic\TTemplateParamClass;
|
|||||||
use Psalm\Type\Atomic\TTemplatePropertiesOf;
|
use Psalm\Type\Atomic\TTemplatePropertiesOf;
|
||||||
use Psalm\Type\Atomic\TTemplateValueOf;
|
use Psalm\Type\Atomic\TTemplateValueOf;
|
||||||
use Psalm\Type\Atomic\TTypeAlias;
|
use Psalm\Type\Atomic\TTypeAlias;
|
||||||
use Psalm\Type\Atomic\TValueOfArray;
|
use Psalm\Type\Atomic\TValueOf;
|
||||||
use Psalm\Type\TypeNode;
|
use Psalm\Type\TypeNode;
|
||||||
use Psalm\Type\Union;
|
use Psalm\Type\Union;
|
||||||
|
|
||||||
@ -743,13 +743,13 @@ class TypeParser
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TKeyOfArray::isViableTemplateType($generic_params[0])) {
|
if (!TKeyOf::isViableTemplateType($generic_params[0])) {
|
||||||
throw new TypeParseTreeException(
|
throw new TypeParseTreeException(
|
||||||
'Untemplated key-of param ' . $param_name . ' should be an array'
|
'Untemplated key-of param ' . $param_name . ' should be an array'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TKeyOfArray($generic_params[0]);
|
return new TKeyOf($generic_params[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($generic_type_value === 'value-of') {
|
if ($generic_type_value === 'value-of') {
|
||||||
@ -765,13 +765,13 @@ class TypeParser
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TValueOfArray::isViableTemplateType($generic_params[0])) {
|
if (!TValueOf::isViableTemplateType($generic_params[0])) {
|
||||||
throw new TypeParseTreeException(
|
throw new TypeParseTreeException(
|
||||||
'Untemplated value-of param ' . $param_name . ' should be an array'
|
'Untemplated value-of param ' . $param_name . ' should be an array'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TValueOfArray($generic_params[0]);
|
return new TValueOf($generic_params[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($generic_type_value === 'int-mask') {
|
if ($generic_type_value === 'int-mask') {
|
||||||
@ -842,8 +842,8 @@ class TypeParser
|
|||||||
$param_type = $param_union_types[0];
|
$param_type = $param_union_types[0];
|
||||||
|
|
||||||
if (!$param_type instanceof TClassConstant
|
if (!$param_type instanceof TClassConstant
|
||||||
&& !$param_type instanceof TValueOfArray
|
&& !$param_type instanceof TValueOf
|
||||||
&& !$param_type instanceof TKeyOfArray
|
&& !$param_type instanceof TKeyOf
|
||||||
) {
|
) {
|
||||||
throw new TypeParseTreeException(
|
throw new TypeParseTreeException(
|
||||||
'Invalid reference passed to int-mask-of'
|
'Invalid reference passed to int-mask-of'
|
||||||
|
@ -11,11 +11,11 @@ use Psalm\Type\Atomic;
|
|||||||
*/
|
*/
|
||||||
final class TIntMaskOf extends TInt
|
final class TIntMaskOf extends TInt
|
||||||
{
|
{
|
||||||
/** @var TClassConstant|TKeyOfArray|TValueOfArray */
|
/** @var TClassConstant|TKeyOf|TValueOf */
|
||||||
public $value;
|
public $value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TClassConstant|TKeyOfArray|TValueOfArray $value
|
* @param TClassConstant|TKeyOf|TValueOf $value
|
||||||
*/
|
*/
|
||||||
public function __construct(Atomic $value)
|
public function __construct(Atomic $value)
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@ use function array_values;
|
|||||||
/**
|
/**
|
||||||
* Represents an offset of an array.
|
* Represents an offset of an array.
|
||||||
*/
|
*/
|
||||||
final class TKeyOfArray extends TArrayKey
|
final class TKeyOf extends TArrayKey
|
||||||
{
|
{
|
||||||
/** @var Union */
|
/** @var Union */
|
||||||
public $type;
|
public $type;
|
@ -9,7 +9,7 @@ use Psalm\Type\Atomic;
|
|||||||
use Psalm\Type\Union;
|
use Psalm\Type\Union;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the type used when using TKeyOfArray when the type of the array is a template
|
* Represents the type used when using TKeyOf when the type of the array is a template
|
||||||
*/
|
*/
|
||||||
final class TTemplateKeyOf extends Atomic
|
final class TTemplateKeyOf extends Atomic
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,7 @@ use Psalm\Type\Atomic;
|
|||||||
use Psalm\Type\Union;
|
use Psalm\Type\Union;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the type used when using TValueOfArray when the type of the array is a template
|
* Represents the type used when using TValueOf when the type of the array or enum is a template
|
||||||
*/
|
*/
|
||||||
final class TTemplateValueOf extends Atomic
|
final class TTemplateValueOf extends Atomic
|
||||||
{
|
{
|
||||||
|
@ -2,16 +2,21 @@
|
|||||||
|
|
||||||
namespace Psalm\Type\Atomic;
|
namespace Psalm\Type\Atomic;
|
||||||
|
|
||||||
|
use Psalm\Codebase;
|
||||||
|
use Psalm\Internal\Codebase\ConstantTypeResolver;
|
||||||
|
use Psalm\Storage\EnumCaseStorage;
|
||||||
use Psalm\Type\Atomic;
|
use Psalm\Type\Atomic;
|
||||||
use Psalm\Type\Union;
|
use Psalm\Type\Union;
|
||||||
|
|
||||||
use function array_merge;
|
use function array_map;
|
||||||
use function array_values;
|
use function array_values;
|
||||||
|
use function assert;
|
||||||
|
use function count;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a value of an array.
|
* Represents a value of an array or enum.
|
||||||
*/
|
*/
|
||||||
final class TValueOfArray extends Atomic
|
final class TValueOf extends Atomic
|
||||||
{
|
{
|
||||||
/** @var Union */
|
/** @var Union */
|
||||||
public $type;
|
public $type;
|
||||||
@ -56,6 +61,7 @@ final class TValueOfArray extends Atomic
|
|||||||
&& !$type instanceof TKeyedArray
|
&& !$type instanceof TKeyedArray
|
||||||
&& !$type instanceof TList
|
&& !$type instanceof TList
|
||||||
&& !$type instanceof TPropertiesOf
|
&& !$type instanceof TPropertiesOf
|
||||||
|
&& !$type instanceof TNamedObject
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -63,39 +69,58 @@ final class TValueOfArray extends Atomic
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getArrayValueType(
|
public static function getValueType(
|
||||||
Union $type,
|
Union $type,
|
||||||
|
Codebase $codebase,
|
||||||
bool $keep_template_params = false
|
bool $keep_template_params = false
|
||||||
): ?Union {
|
): ?Union {
|
||||||
$value_types = [];
|
$value_types = [];
|
||||||
|
|
||||||
foreach ($type->getAtomicTypes() as $atomic_type) {
|
foreach ($type->getAtomicTypes() as $atomic_type) {
|
||||||
if ($atomic_type instanceof TArray) {
|
if ($atomic_type instanceof TArray) {
|
||||||
$array_value_atomics = $atomic_type->type_params[1];
|
$value_atomics = $atomic_type->type_params[1];
|
||||||
} elseif ($atomic_type instanceof TList) {
|
} elseif ($atomic_type instanceof TList) {
|
||||||
$array_value_atomics = $atomic_type->type_param;
|
$value_atomics = $atomic_type->type_param;
|
||||||
} elseif ($atomic_type instanceof TKeyedArray) {
|
} elseif ($atomic_type instanceof TKeyedArray) {
|
||||||
$array_value_atomics = $atomic_type->getGenericValueType();
|
$value_atomics = $atomic_type->getGenericValueType();
|
||||||
} elseif ($atomic_type instanceof TTemplateParam) {
|
} elseif ($atomic_type instanceof TTemplateParam) {
|
||||||
if ($keep_template_params) {
|
if ($keep_template_params) {
|
||||||
$array_value_atomics = new Union([$atomic_type]);
|
$value_atomics = new Union([$atomic_type]);
|
||||||
} else {
|
} else {
|
||||||
$array_value_atomics = static::getArrayValueType(
|
$value_atomics = static::getValueType(
|
||||||
$atomic_type->as,
|
$atomic_type->as,
|
||||||
|
$codebase,
|
||||||
$keep_template_params
|
$keep_template_params
|
||||||
);
|
);
|
||||||
if ($array_value_atomics === null) {
|
if ($value_atomics === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} elseif ($atomic_type instanceof TNamedObject
|
||||||
|
&& $codebase->classlike_storage_provider->has($atomic_type->value)
|
||||||
|
) {
|
||||||
|
$class_storage = $codebase->classlike_storage_provider->get($atomic_type->value);
|
||||||
|
$cases = $class_storage->enum_cases;
|
||||||
|
if (!$class_storage->is_enum
|
||||||
|
|| $class_storage->enum_type === null
|
||||||
|
|| count($cases) === 0
|
||||||
|
) {
|
||||||
|
// Invalid value-of, skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value_atomics = new Union(array_map(
|
||||||
|
function (EnumCaseStorage $case): Atomic {
|
||||||
|
assert($case->value !== null); // Backed enum must have a value
|
||||||
|
return ConstantTypeResolver::getLiteralTypeFromScalarValue($case->value);
|
||||||
|
},
|
||||||
|
array_values($cases),
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$value_types = array_merge(
|
$value_types = [...$value_types, ...array_values($value_atomics->getAtomicTypes())];
|
||||||
$value_types,
|
|
||||||
array_values($array_value_atomics->getAtomicTypes())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($value_types === []) {
|
if ($value_types === []) {
|
@ -7,7 +7,7 @@ namespace Psalm\Tests;
|
|||||||
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
||||||
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
||||||
|
|
||||||
class ValueOfArrayTest extends TestCase
|
class ValueOfTest extends TestCase
|
||||||
{
|
{
|
||||||
use InvalidCodeAnalysisTestTrait;
|
use InvalidCodeAnalysisTestTrait;
|
||||||
use ValidCodeAnalysisTestTrait;
|
use ValidCodeAnalysisTestTrait;
|
||||||
@ -141,6 +141,74 @@ class ValueOfArrayTest extends TestCase
|
|||||||
}
|
}
|
||||||
',
|
',
|
||||||
],
|
],
|
||||||
|
'valueOfStringEnum' => [
|
||||||
|
'code' => '<?php
|
||||||
|
enum Foo: string
|
||||||
|
{
|
||||||
|
case Foo = "foo";
|
||||||
|
case Bar = "bar";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param value-of<Foo> $arg */
|
||||||
|
function foobar(string $arg): void
|
||||||
|
{
|
||||||
|
/** @psalm-check-type-exact $arg = "foo"|"bar" */;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Foo */
|
||||||
|
$foo = Foo::Foo;
|
||||||
|
foobar($foo->value);
|
||||||
|
',
|
||||||
|
'assertions' => [],
|
||||||
|
'ignored_issues' => [],
|
||||||
|
'php_version' => '8.1',
|
||||||
|
],
|
||||||
|
'valueOfIntEnum' => [
|
||||||
|
'code' => '<?php
|
||||||
|
enum Foo: int
|
||||||
|
{
|
||||||
|
case Foo = 2;
|
||||||
|
case Bar = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param value-of<Foo> $arg */
|
||||||
|
function foobar(int $arg): void
|
||||||
|
{
|
||||||
|
/** @psalm-check-type-exact $arg = 2|3 */;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Foo */
|
||||||
|
$foo = Foo::Foo;
|
||||||
|
foobar($foo->value);
|
||||||
|
',
|
||||||
|
'assertions' => [],
|
||||||
|
'ignored_issues' => [],
|
||||||
|
'php_version' => '8.1',
|
||||||
|
],
|
||||||
|
'valueOfEnumUnion' => [
|
||||||
|
'code' => '<?php
|
||||||
|
enum Foo: int
|
||||||
|
{
|
||||||
|
case Foo = 2;
|
||||||
|
case Bar = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Bar: string
|
||||||
|
{
|
||||||
|
case Foo = "foo";
|
||||||
|
case Bar = "bar";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param value-of<Foo|Bar> $arg */
|
||||||
|
function foobar(int|string $arg): void
|
||||||
|
{
|
||||||
|
/** @psalm-check-type-exact $arg = 2|3|"foo"|"bar" */;
|
||||||
|
}
|
||||||
|
',
|
||||||
|
'assertions' => [],
|
||||||
|
'ignored_issues' => [],
|
||||||
|
'php_version' => '8.1',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +280,23 @@ class ValueOfArrayTest extends TestCase
|
|||||||
',
|
',
|
||||||
'error_message' => 'InvalidReturnStatement'
|
'error_message' => 'InvalidReturnStatement'
|
||||||
],
|
],
|
||||||
|
'valueOfUnitEnum' => [
|
||||||
|
'code' => '<?php
|
||||||
|
enum Foo
|
||||||
|
{
|
||||||
|
case Foo;
|
||||||
|
case Bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param value-of<Foo> $arg */
|
||||||
|
function foobar(string $arg): void {}
|
||||||
|
',
|
||||||
|
// TODO turn this into an InvalidDocblock with a better error message. This is difficult because it
|
||||||
|
// has to happen after scanning has finished, otherwise the class might not have been scanned yet.
|
||||||
|
'error_message' => 'MismatchingDocblockParamType',
|
||||||
|
'ignored_issues' => [],
|
||||||
|
'php_version' => '8.1',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user