From c0864b2652f0e497335858a6d58d0e9c44798dd4 Mon Sep 17 00:00:00 2001 From: orklah Date: Fri, 7 Jan 2022 15:00:14 +0100 Subject: [PATCH 1/4] emit errors on failure to reconcile ints --- .../Type/SimpleAssertionReconciler.php | 167 ++++++++++++++--- .../Type/SimpleNegatedAssertionReconciler.php | 169 +++++++++++++++--- src/Psalm/Type/Reconciler.php | 7 + src/Psalm/Type/Union.php | 5 + 4 files changed, 296 insertions(+), 52 deletions(-) diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index 2c6562fc221..c90eeeeb54f 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -35,6 +35,7 @@ use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNonEmptyLowercaseString; @@ -91,6 +92,8 @@ public static function reconcile( return $existing_var_type; } + $old_var_type_string = $existing_var_type->getId(); + if ($assertion === 'isset') { return self::reconcileIsset( $existing_var_type, @@ -133,16 +136,26 @@ public static function reconcile( if ($assertion[0] === '>') { return self::reconcileSuperiorTo( $existing_var_type, - substr($assertion, 1), - $inside_loop + $assertion, + $inside_loop, + $old_var_type_string, + $key, + $negated, + $code_location, + $suppressed_issues ); } if ($assertion[0] === '<') { return self::reconcileInferiorTo( $existing_var_type, - substr($assertion, 1), - $inside_loop + $assertion, + $inside_loop, + $old_var_type_string, + $key, + $negated, + $code_location, + $suppressed_issues ); } @@ -1616,30 +1629,45 @@ private static function reconcileHasArrayKey( return $existing_var_type; } + /** + * @param string[] $suppressed_issues + */ private static function reconcileSuperiorTo( - Union $existing_var_type, - string $assertion, - bool $inside_loop + Union $existing_var_type, + string $assertion, + bool $inside_loop, + string $old_var_type_string, + ?string $var_id, + bool $negated, + ?CodeLocation $code_location, + array $suppressed_issues ): Union { - $assertion_value = (int)$assertion; + $assertion_value = (int)substr($assertion, 1); + + $did_remove_type = false; + foreach ($existing_var_type->getAtomicTypes() as $atomic_type) { if ($inside_loop) { continue; } if ($atomic_type instanceof TIntRange) { - $existing_var_type->removeType($atomic_type->getKey()); - if ($atomic_type->min_bound === null) { - $atomic_type->min_bound = $assertion_value; - } else { - $atomic_type->min_bound = TIntRange::getNewHighestBound( - $assertion_value, - $atomic_type->min_bound - ); + if ($atomic_type->contains($assertion_value)) { + $did_remove_type = true; + $existing_var_type->removeType($atomic_type->getKey()); + if ($atomic_type->min_bound === null) { + $atomic_type->min_bound = $assertion_value; + } else { + $atomic_type->min_bound = TIntRange::getNewHighestBound( + $assertion_value, + $atomic_type->min_bound + ); + } + $existing_var_type->addType($atomic_type); } - $existing_var_type->addType($atomic_type); } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value < $assertion_value) { + $did_remove_type = true; $existing_var_type->removeType($atomic_type->getKey()); } /*elseif ($inside_loop) { //when inside a loop, allow the range to extends the type @@ -1652,39 +1680,89 @@ private static function reconcileSuperiorTo( }*/ } 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()); $existing_var_type->addType(new TIntRange($assertion_value, null)); + } else { + // we assume that other types may have been removed (empty strings? numeric strings?) + //It may be worth refining to improve reconciliation while keeping in mind we're on loose comparison + $did_remove_type = true; + } + } + + if (!$inside_loop && !$did_remove_type && $var_id && $code_location) { + self::triggerIssueForImpossible( + $existing_var_type, + $old_var_type_string, + $var_id, + $assertion, + true, + $negated, + $code_location, + $suppressed_issues + ); + } + + if ($existing_var_type->isUnionEmpty()) { + if ($var_id && $code_location) { + self::triggerIssueForImpossible( + $existing_var_type, + $old_var_type_string, + $var_id, + $assertion, + false, + $negated, + $code_location, + $suppressed_issues + ); } + $existing_var_type->addType(new TNever()); } return $existing_var_type; } + /** + * @param string[] $suppressed_issues + */ private static function reconcileInferiorTo( - Union $existing_var_type, - string $assertion, - bool $inside_loop + Union $existing_var_type, + string $assertion, + bool $inside_loop, + string $old_var_type_string, + ?string $var_id, + bool $negated, + ?CodeLocation $code_location, + array $suppressed_issues ): Union { - $assertion_value = (int)$assertion; + $assertion_value = (int)substr($assertion, 1); + + $did_remove_type = false; + foreach ($existing_var_type->getAtomicTypes() as $atomic_type) { if ($inside_loop) { continue; } if ($atomic_type instanceof TIntRange) { - $existing_var_type->removeType($atomic_type->getKey()); - if ($atomic_type->max_bound === null) { - $atomic_type->max_bound = $assertion_value; - } else { - $atomic_type->max_bound = min($atomic_type->max_bound, $assertion_value); + if ($atomic_type->contains($assertion_value)) { + $did_remove_type = true; + $existing_var_type->removeType($atomic_type->getKey()); + if ($atomic_type->max_bound === null) { + $atomic_type->max_bound = $assertion_value; + } else { + $atomic_type->max_bound = min($atomic_type->max_bound, $assertion_value); + } + $existing_var_type->addType($atomic_type); } - $existing_var_type->addType($atomic_type); } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value > $assertion_value) { + $did_remove_type = true; $existing_var_type->removeType($atomic_type->getKey()); } /* elseif ($inside_loop) { //when inside a loop, allow the range to extends the type @@ -1696,14 +1774,49 @@ private static function reconcileInferiorTo( } }*/ } 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()); $existing_var_type->addType(new TIntRange(null, $assertion_value)); + } else { + // we assume that other types may have been removed (empty strings? numeric strings?) + //It may be worth refining to improve reconciliation while keeping in mind we're on loose comparison + $did_remove_type = true; + } + } + + if (!$inside_loop && !$did_remove_type && $var_id && $code_location) { + self::triggerIssueForImpossible( + $existing_var_type, + $old_var_type_string, + $var_id, + $assertion, + true, + $negated, + $code_location, + $suppressed_issues + ); + } + + if ($existing_var_type->isUnionEmpty()) { + if ($var_id && $code_location) { + self::triggerIssueForImpossible( + $existing_var_type, + $old_var_type_string, + $var_id, + $assertion, + false, + $negated, + $code_location, + $suppressed_issues + ); } + $existing_var_type->addType(new TNever()); } return $existing_var_type; diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index f3d8591f53f..63d77ce589e 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -31,6 +31,7 @@ use Psalm\Type\Atomic\TLowercaseString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNonEmptyLowercaseString; @@ -73,6 +74,8 @@ public static function reconcile( bool $is_equality = false, bool $inside_loop = false ): ?Union { + $old_var_type_string = $existing_var_type->getId(); + if ($assertion === 'isset') { if ($existing_var_type->possibly_undefined) { return Type::getEmpty(); @@ -352,16 +355,26 @@ public static function reconcile( if ($assertion[0] === '>') { return self::reconcileSuperiorTo( $existing_var_type, - substr($assertion, 1), - $inside_loop + $assertion, + $inside_loop, + $old_var_type_string, + $key, + $negated, + $code_location, + $suppressed_issues ); } if ($assertion[0] === '<') { return self::reconcileInferiorTo( $existing_var_type, - substr($assertion, 1), - $inside_loop + $assertion, + $inside_loop, + $old_var_type_string, + $key, + $negated, + $code_location, + $suppressed_issues ); } @@ -1601,27 +1614,45 @@ private static function reconcileResource( return Type::getMixed(); } - private static function reconcileSuperiorTo(Union $existing_var_type, string $assertion, bool $inside_loop): Union - { - $assertion_value = (int)$assertion - 1; + /** + * @param string[] $suppressed_issues + */ + private static function reconcileSuperiorTo( + Union $existing_var_type, + string $assertion, + bool $inside_loop, + string $old_var_type_string, + ?string $var_id, + bool $negated, + ?CodeLocation $code_location, + array $suppressed_issues + ): Union { + $assertion_value = (int)substr($assertion, 1) - 1; + + $did_remove_type = false; + foreach ($existing_var_type->getAtomicTypes() as $atomic_type) { if ($inside_loop) { continue; } if ($atomic_type instanceof TIntRange) { - $existing_var_type->removeType($atomic_type->getKey()); - if ($atomic_type->max_bound === null) { - $atomic_type->max_bound = $assertion_value; - } else { - $atomic_type->max_bound = TIntRange::getNewLowestBound( - $assertion_value, - $atomic_type->max_bound - ); + if ($atomic_type->contains($assertion_value)) { + $did_remove_type = true; + $existing_var_type->removeType($atomic_type->getKey()); + if ($atomic_type->max_bound === null) { + $atomic_type->max_bound = $assertion_value; + } else { + $atomic_type->max_bound = TIntRange::getNewLowestBound( + $assertion_value, + $atomic_type->max_bound + ); + } + $existing_var_type->addType($atomic_type); } - $existing_var_type->addType($atomic_type); } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value > $assertion_value) { + $did_remove_type = true; $existing_var_type->removeType($atomic_type->getKey()); } /*elseif ($inside_loop) { //when inside a loop, allow the range to extends the type @@ -1633,37 +1664,90 @@ private static function reconcileSuperiorTo(Union $existing_var_type, string $as } }*/ } 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()); $existing_var_type->addType(new TIntRange(null, $assertion_value)); + } else { + // we assume that other types may have been removed (empty strings? numeric strings?) + //It may be worth refining to improve reconciliation while keeping in mind we're on loose comparison + $did_remove_type = true; + } + } + + if (!$inside_loop && !$did_remove_type && $var_id && $code_location) { + self::triggerIssueForImpossible( + $existing_var_type, + $old_var_type_string, + $var_id, + '!'.$assertion, + true, + $negated, + $code_location, + $suppressed_issues + ); + } + + if ($existing_var_type->isUnionEmpty()) { + if ($var_id && $code_location) { + self::triggerIssueForImpossible( + $existing_var_type, + $old_var_type_string, + $var_id, + '!'.$assertion, + false, + $negated, + $code_location, + $suppressed_issues + ); } + $existing_var_type->addType(new TNever()); } return $existing_var_type; } - private static function reconcileInferiorTo(Union $existing_var_type, string $assertion, bool $inside_loop): Union - { - $assertion_value = (int)$assertion + 1; + /** + * @param string[] $suppressed_issues + */ + private static function reconcileInferiorTo( + Union $existing_var_type, + string $assertion, + bool $inside_loop, + string $old_var_type_string, + ?string $var_id, + bool $negated, + ?CodeLocation $code_location, + array $suppressed_issues + ): Union { + $assertion_value = (int)substr($assertion, 1) + 1; + + $did_remove_type = false; + foreach ($existing_var_type->getAtomicTypes() as $atomic_type) { if ($inside_loop) { continue; } if ($atomic_type instanceof TIntRange) { - $existing_var_type->removeType($atomic_type->getKey()); - if ($atomic_type->min_bound === null) { - $atomic_type->min_bound = $assertion_value; - } else { - $atomic_type->min_bound = max($atomic_type->min_bound, $assertion_value); + if ($atomic_type->contains($assertion_value)) { + $did_remove_type = true; + $existing_var_type->removeType($atomic_type->getKey()); + if ($atomic_type->min_bound === null) { + $atomic_type->min_bound = $assertion_value; + } else { + $atomic_type->min_bound = max($atomic_type->min_bound, $assertion_value); + } + $existing_var_type->addType($atomic_type); } - $existing_var_type->addType($atomic_type); } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value < $assertion_value) { + $did_remove_type = true; $existing_var_type->removeType($atomic_type->getKey()); } /* elseif ($inside_loop) { //when inside a loop, allow the range to extends the type @@ -1676,13 +1760,48 @@ private static function reconcileInferiorTo(Union $existing_var_type, string $as }*/ } 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()); $existing_var_type->addType(new TIntRange($assertion_value, null)); + } else { + // we assume that other types may have been removed (empty strings? numeric strings?) + //It may be worth refining to improve reconciliation while keeping in mind we're on loose comparison + $did_remove_type = true; + } + } + + if (!$inside_loop && !$did_remove_type && $var_id && $code_location) { + self::triggerIssueForImpossible( + $existing_var_type, + $old_var_type_string, + $var_id, + '!'.$assertion, + true, + $negated, + $code_location, + $suppressed_issues + ); + } + + if ($existing_var_type->isUnionEmpty()) { + if ($var_id && $code_location) { + self::triggerIssueForImpossible( + $existing_var_type, + $old_var_type_string, + $var_id, + '!'.$assertion, + false, + $negated, + $code_location, + $suppressed_issues + ); } + $existing_var_type->addType(new TNever()); } return $existing_var_type; diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index 67a828515a9..218b9b548e7 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -892,6 +892,13 @@ protected static function triggerIssueForImpossible( $assertion = substr($assertion, 1); } + $operator = substr($assertion, 0, 1); + if ($operator === '>') { + $assertion = '>= '.substr($assertion, 1); + } elseif ($operator === '<') { + $assertion = '<= '.substr($assertion, 1); + } + if ($negated) { $redundant = !$redundant; $not = !$not; diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index d43fb967936..16d1682ae61 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -1605,4 +1605,9 @@ public function getSingleAtomic(): Atomic { return reset($this->types); } + + public function isUnionEmpty(): bool + { + return $this->types === []; + } } From af28687708740335d3add1ac3ca421672dc3a2a0 Mon Sep 17 00:00:00 2001 From: orklah Date: Fri, 7 Jan 2022 19:38:15 +0100 Subject: [PATCH 2/4] fix reconciliation when the assertions is not part of the existing range and add tests --- .../Type/SimpleAssertionReconciler.php | 14 +++++++++ .../Type/SimpleNegatedAssertionReconciler.php | 14 +++++++++ src/Psalm/Type/Atomic/TIntRange.php | 16 ++++++++++ tests/IntRangeTest.php | 30 +++++++++++++++++++ 4 files changed, 74 insertions(+) diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index c90eeeeb54f..9865e5d4bf0 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -1653,6 +1653,7 @@ private static function reconcileSuperiorTo( if ($atomic_type instanceof TIntRange) { if ($atomic_type->contains($assertion_value)) { + // if the range contains the assertion, the range must be adapted $did_remove_type = true; $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->min_bound === null) { @@ -1664,6 +1665,12 @@ private static function reconcileSuperiorTo( ); } $existing_var_type->addType($atomic_type); + } elseif ($atomic_type->isLesserThan($assertion_value)) { + // if the range is lesser than the assertion, the type must be removed + $did_remove_type = true; + $existing_var_type->removeType($atomic_type->getKey()); + } elseif ($atomic_type->isGreaterThan($assertion_value)) { + // if the range is greater than the assertion, the check is redundant } } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value < $assertion_value) { @@ -1751,6 +1758,7 @@ private static function reconcileInferiorTo( if ($atomic_type instanceof TIntRange) { if ($atomic_type->contains($assertion_value)) { + // if the range contains the assertion, the range must be adapted $did_remove_type = true; $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->max_bound === null) { @@ -1759,6 +1767,12 @@ private static function reconcileInferiorTo( $atomic_type->max_bound = min($atomic_type->max_bound, $assertion_value); } $existing_var_type->addType($atomic_type); + } elseif ($atomic_type->isLesserThan($assertion_value)) { + // if the range is lesser than the assertion, the check is redundant + } elseif ($atomic_type->isGreaterThan($assertion_value)) { + // if the range is greater than the assertion, the type must be removed + $did_remove_type = true; + $existing_var_type->removeType($atomic_type->getKey()); } } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value > $assertion_value) { diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index 63d77ce589e..5fdc2508ab4 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -1638,6 +1638,7 @@ private static function reconcileSuperiorTo( if ($atomic_type instanceof TIntRange) { if ($atomic_type->contains($assertion_value)) { + // if the range contains the assertion, the range must be adapted $did_remove_type = true; $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->max_bound === null) { @@ -1649,6 +1650,12 @@ private static function reconcileSuperiorTo( ); } $existing_var_type->addType($atomic_type); + } elseif ($atomic_type->isLesserThan($assertion_value)) { + // if the range is lesser than the assertion, the check is redundant + } elseif ($atomic_type->isGreaterThan($assertion_value)) { + // if the range is greater than the assertion, the type must be removed + $did_remove_type = true; + $existing_var_type->removeType($atomic_type->getKey()); } } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value > $assertion_value) { @@ -1736,6 +1743,7 @@ private static function reconcileInferiorTo( if ($atomic_type instanceof TIntRange) { if ($atomic_type->contains($assertion_value)) { + // if the range contains the assertion, the range must be adapted $did_remove_type = true; $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->min_bound === null) { @@ -1744,6 +1752,12 @@ private static function reconcileInferiorTo( $atomic_type->min_bound = max($atomic_type->min_bound, $assertion_value); } $existing_var_type->addType($atomic_type); + } elseif ($atomic_type->isLesserThan($assertion_value)) { + // if the range is lesser than the assertion, the type must be removed + $did_remove_type = true; + $existing_var_type->removeType($atomic_type->getKey()); + } elseif ($atomic_type->isGreaterThan($assertion_value)) { + // if the range is greater than the assertion, the check is redundant } } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value < $assertion_value) { diff --git a/src/Psalm/Type/Atomic/TIntRange.php b/src/Psalm/Type/Atomic/TIntRange.php index 5571823f271..5b2adcddf52 100644 --- a/src/Psalm/Type/Atomic/TIntRange.php +++ b/src/Psalm/Type/Atomic/TIntRange.php @@ -86,6 +86,22 @@ public function contains(int $i): bool ($this->min_bound <= $i && $this->max_bound >= $i); } + /** + * Returns true if every part of the Range is lesser than the given value + */ + public function isLesserThan(int $i): bool + { + return $this->max_bound !== null && $this->max_bound < $i; + } + + /** + * Returns true if every part of the Range is greater than the given value + */ + public function isGreaterThan(int $i): bool + { + return $this->min_bound !== null && $this->min_bound > $i; + } + public static function getNewLowestBound(?int $bound1, ?int $bound2): ?int { if ($bound1 === null || $bound2 === null) { diff --git a/tests/IntRangeTest.php b/tests/IntRangeTest.php index 5b7cf913eaf..f5eb50bb8d3 100644 --- a/tests/IntRangeTest.php +++ b/tests/IntRangeTest.php @@ -702,6 +702,36 @@ function scope(int $a){ }', 'error_message' => 'InvalidReturnType', ], + 'assertOutOfRange' => [ + ' $a + */ + function scope(int $a): void{ + assert($a === 0); + }', + 'error_message' => 'DocblockTypeContradiction', + ], + 'assertRedundantInferior' => [ + ' $a + */ + function scope(int $a): void{ + assert($a < 10); + }', + 'error_message' => 'RedundantConditionGivenDocblockType', + ], + 'assertImpossibleInferior' => [ + ' $a + */ + function scope(int $a): void{ + assert($a < 4); + }', + 'error_message' => 'DocblockTypeContradiction', + ], ]; } } From 5ce626fcdfa9b8256a4ef589e6858fcbd83ee632 Mon Sep 17 00:00:00 2001 From: orklah Date: Fri, 7 Jan 2022 15:33:05 +0100 Subject: [PATCH 3/4] fix test --- tests/PureAnnotationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PureAnnotationTest.php b/tests/PureAnnotationTest.php index 988459f9167..6853b32ea77 100644 --- a/tests/PureAnnotationTest.php +++ b/tests/PureAnnotationTest.php @@ -323,7 +323,7 @@ function sumExpectedToNotBlowPowerFuse(int $first, int $second): int { if ($sum > 9000) { throw MyException::hello(); } - if ($sum > 90001) { + if ($sum > 900) { throw new MyException(); } return $sum; From cc529e8a617c4b13a607065a93f8c9374b77be92 Mon Sep 17 00:00:00 2001 From: orklah Date: Fri, 7 Jan 2022 15:58:02 +0100 Subject: [PATCH 4/4] refresh baseline --- psalm-baseline.xml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 89513d70bb3..a3054781035 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + $comment_block->tags['variablesfrom'][0] @@ -421,6 +421,13 @@ $stmt->props[0] + + + $type < 1 + $type < 1 || $type > 4 + $type > 4 + + $pair[1]