Skip to content

Commit

Permalink
Merge pull request #9031 from danog/misc_fixes
Browse files Browse the repository at this point in the history
Misc fixes
  • Loading branch information
orklah committed Dec 30, 2022
2 parents 6353143 + 4b00095 commit 406946d
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 288 deletions.
4 changes: 3 additions & 1 deletion docs/annotating_code/type_syntax/atomic_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Atomic types are the basic building block of all type information used in Psalm. Multiple atomic types can be combined, either with [union types](union_types.md) or [intersection types](intersection_types.md). Psalm allows many different sorts of atomic types to be expressed in docblock syntax:

**Note**: you can view detailed documentation and usage examples for all atomic types by clicking on each type in the following list.

* [Scalar types](scalar_types.md)
* [bool](scalar_types.md#scalar)
* [int](scalar_types.md#scalar)
Expand All @@ -26,7 +28,7 @@ Atomic types are the basic building block of all type information used in Psalm.
* [Exception, Foo\MyClass and `Foo\MyClass<Bar>`](object_types.md#named-objectsmd)
* [Generator](object_types.md#generators)
* [Array types](array_types.md)
* [array&lt;int, string&gt; ](array_types.md#generic-arrays)
* [array&lt;int, string&gt;](array_types.md#generic-arrays)
* [non-empty-array](array_types.md#non-empty-array)
* [string\[\]](array_types.md#phpdoc-syntax)
* [list & non-empty-list](array_types.md#lists)
Expand Down
14 changes: 8 additions & 6 deletions docs/annotating_code/type_syntax/utility_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Psalm supports some _magical_ utility types that brings superpower to the PHP ty
The `key-of` utility returns the offset-type for any [array type](array_types.md).

Some examples:

- `key-of<Foo\Bar::ARRAY_CONST>` evaluates to offset-type of `ARRAY_CONST` (Psalm 3.3+)
- `key-of<list<mixed>>` evaluates to `int`
- `key-of<array{a: mixed, b: mixed}|array{c: mixed}>` evaluates to `'a'|'b'|'c'`
Expand All @@ -18,6 +19,7 @@ Some examples:
### Notes on template usage

If you use `key-of` with a template param, you can fulfill the type check only with these allowed methods:

- `array_keys($t)`
- `array_key_first($t)`
- `array_key_last($t)`
Expand All @@ -35,14 +37,14 @@ function getKeys($array) {
}
```


## `value-of<T>`
## value-of&lt;T&gt;

(Psalm 5.0+)

The `value-of` utility returns the value-type for any [array type](array_types.md).

Some examples:

- `value-of<Foo\Bar::ARRAY_CONST>` evaluates to value-type of `ARRAY_CONST` (Psalm 3.3+)
- `value-of<list<float>>` evaluates to `float`
- `value-of<array{a: bool, b: int}|array{c: string}>` evaluates to `bool|int|string`
Expand All @@ -52,6 +54,7 @@ Some examples:
### Notes on template usage

If you use `value-of` with a template param, you can fulfill the type check only with these allowed methods:

- `array_values`

```php
Expand All @@ -67,8 +70,7 @@ function getValues($array) {

Currently `in_array($value, $t)` **does not** infer that `$value` is of `value-of<T>`.


## `properties-of<T>`
## properties-of&lt;T&gt;

(Psalm 5.0+)

Expand Down Expand Up @@ -103,11 +105,11 @@ class A {

Note that `properties-of<T>` will return **all non-static** properties. There are the following subtypes to pick only
properties with a certain visibility:

- `public-properties-of<T>`
- `protected-properties-of<T>`
- `private-properties-of<T>`


### Sealed array support

Use final classes if you want to properties-of and get_object_vars to return sealed arrays:
Expand Down Expand Up @@ -139,7 +141,7 @@ $b = asArray(new B);
/** @psalm-trace $b */; // array{foo: string, bar: int, baz: float}
```

## `class-string-map<T as Foo, T>`
## class-string-map&lt;T as Foo, T&gt;

Used to indicate an array where each value is equal an instance of the class string contained in the key:

Expand Down
8 changes: 4 additions & 4 deletions src/Psalm/Internal/Type/AssertionReconciler.php
Original file line number Diff line number Diff line change
Expand Up @@ -934,20 +934,20 @@ private static function handleLiteralEquality(
}

$can_be_equal = false;
$did_remove_type = false;
$redundant = true;

$existing_var_type = $existing_var_type->getBuilder();
foreach ($existing_var_atomic_types as $atomic_key => $atomic_type) {
if (get_class($atomic_type) === TNamedObject::class
&& $atomic_type->value === $fq_enum_name
) {
$can_be_equal = true;
$did_remove_type = true;
$redundant = false;
$existing_var_type->removeType($atomic_key);
$existing_var_type->addType(new TEnumCase($fq_enum_name, $case_name));
} elseif ($atomic_key !== $assertion_type->getKey()) {
$existing_var_type->removeType($atomic_key);
$did_remove_type = true;
$redundant = false;
} else {
$can_be_equal = true;
}
Expand All @@ -956,7 +956,7 @@ private static function handleLiteralEquality(

if ($var_id
&& $code_location
&& (!$can_be_equal || (!$did_remove_type && count($existing_var_atomic_types) === 1))
&& (!$can_be_equal || ($redundant && count($existing_var_atomic_types) === 1))
) {
self::triggerIssueForImpossible(
$existing_var_type,
Expand Down
20 changes: 10 additions & 10 deletions src/Psalm/Internal/Type/NegatedAssertionReconciler.php
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ private static function handleLiteralNegatedEquality(
$existing_var_type = $existing_var_type->getBuilder();
$existing_var_atomic_types = $existing_var_type->getAtomicTypes();

$did_remove_type = false;
$redundant = true;
$did_match_literal_type = false;

$scalar_var_type = null;
Expand All @@ -327,7 +327,7 @@ private static function handleLiteralNegatedEquality(
$did_match_literal_type = true;

if ($existing_var_type->removeType($assertion_type->getKey())) {
$did_remove_type = true;
$redundant = false;
}
}

Expand All @@ -336,7 +336,7 @@ private static function handleLiteralNegatedEquality(
if ($existing_range_types) {
foreach ($existing_range_types as $int_key => $literal_type) {
if ($literal_type->contains($assertion_type->value)) {
$did_remove_type = true;
$redundant = false;
$existing_var_type->removeType($int_key);
if ($literal_type->min_bound === null
|| $literal_type->min_bound <= $assertion_type->value - 1
Expand All @@ -361,7 +361,7 @@ private static function handleLiteralNegatedEquality(
if (isset($existing_var_type->getAtomicTypes()['int'])
&& get_class($existing_var_type->getAtomicTypes()['int']) === Type\Atomic\TInt::class
) {
$did_remove_type = true;
$redundant = false;
//this may be used to generate a range containing any int except the one that was asserted against
//but this is failing some tests
/*$existing_var_type->removeType('int');
Expand All @@ -377,7 +377,7 @@ private static function handleLiteralNegatedEquality(
$did_match_literal_type = true;

if ($existing_var_type->removeType($assertion_type->getKey())) {
$did_remove_type = true;
$redundant = false;
}
} elseif ($assertion_type->value === "") {
$existing_var_type->addType(new TNonEmptyString());
Expand All @@ -391,7 +391,7 @@ private static function handleLiteralNegatedEquality(
$did_match_literal_type = true;

if ($existing_var_type->removeType($assertion_type->getKey())) {
$did_remove_type = true;
$redundant = false;
}
}
} else {
Expand All @@ -413,7 +413,7 @@ private static function handleLiteralNegatedEquality(
$scalar_var_type = $assertion_type;
} else {
$existing_var_type->removeType($atomic_type->getKey());
$did_remove_type = true;
$redundant = false;

foreach ($enum_storage->enum_cases as $alt_case_name => $_) {
if ($alt_case_name === $case_name) {
Expand All @@ -430,7 +430,7 @@ private static function handleLiteralNegatedEquality(
$did_match_literal_type = true;
} elseif ($atomic_key === $assertion_type->getKey()) {
$existing_var_type->removeType($assertion_type->getKey());
$did_remove_type = true;
$redundant = false;
}
}
}
Expand All @@ -439,14 +439,14 @@ private static function handleLiteralNegatedEquality(

if ($key && $code_location) {
if ($did_match_literal_type
&& (!$did_remove_type || count($existing_var_atomic_types) === 1)
&& ($redundant || count($existing_var_atomic_types) === 1)
) {
self::triggerIssueForImpossible(
$existing_var_type,
$old_var_type_string,
$key,
$assertion,
!$did_remove_type,
$redundant,
$negated,
$code_location,
$suppressed_issues,
Expand Down

0 comments on commit 406946d

Please sign in to comment.