Skip to content

Commit

Permalink
Assert intersection type when two variables are compared (#5774)
Browse files Browse the repository at this point in the history
  • Loading branch information
orklah committed May 18, 2021
1 parent 3c43bc1 commit f62b83a
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 4 deletions.
4 changes: 0 additions & 4 deletions src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php
Expand Up @@ -596,10 +596,6 @@ public static function checkPropertyVisibility(
return $emit_issues ? null : true;

case self::VISIBILITY_PROTECTED:
if ($appearing_property_class === $context->self) {
return null;
}

if (!$context->self) {
if ($emit_issues && IssueBuffer::accepts(
new InaccessibleProperty(
Expand Down
Expand Up @@ -437,6 +437,40 @@ private static function scrapeEqualityAssertions(
)) {
// fall through
}
} else {
// both side of the Identical can be asserted to the intersection of both
$intersection_type = Type::intersectUnionTypes($var_type, $other_type, $codebase);

if ($intersection_type !== null && $intersection_type->isSingle()) {
$assertion = $intersection_type->getAssertionString();

$if_types = [];

$var_name_left = ExpressionIdentifier::getArrayVarId(
$conditional->left,
$this_class_name,
$source
);

if ($var_name_left &&
(!$var_type->isSingle() || $var_type->getAssertionString() !== $assertion)) {
$if_types[$var_name_left] = [['~'.$assertion]];
}

$var_name_right = ExpressionIdentifier::getArrayVarId(
$conditional->right,
$this_class_name,
$source
);

if ($var_name_right &&
(!$other_type->isSingle() || $other_type->getAssertionString() !== $assertion)) {
$if_types[$var_name_right] = [['~'.$assertion]];
}

return $if_types ? [$if_types] : [];
}

}
}

Expand Down
14 changes: 14 additions & 0 deletions src/Psalm/Type.php
@@ -1,6 +1,7 @@
<?php
namespace Psalm;

use Psalm\Internal\Type\Comparator\UnionTypeComparator;
use Psalm\Plugin\EventHandler\Event\StringInterpreterEvent;
use Psalm\Type\Atomic\TNever;
use function array_merge;
Expand Down Expand Up @@ -649,6 +650,19 @@ public static function intersectUnionTypes(
}
}

//if a type is contained by the other, the intersection is the narrowest type
if (!$intersection_performed) {
$type_1_in_2 = UnionTypeComparator::isContainedBy($codebase, $type_1, $type_2);
$type_2_in_1 = UnionTypeComparator::isContainedBy($codebase, $type_2, $type_1);
if ($type_1_in_2) {
$intersection_performed = true;
$combined_type = $type_1;
} elseif ($type_2_in_1) {
$intersection_performed = true;
$combined_type = $type_2;
}
}

if (!$type_1->initialized && !$type_2->initialized) {
$combined_type->initialized = false;
}
Expand Down
9 changes: 9 additions & 0 deletions tests/TypeReconciliation/TypeAlgebraTest.php
Expand Up @@ -1093,6 +1093,15 @@ function foo(?X $x): void {
[],
'8.1',
],
'narrowedTypeAfterIdenticalCheckWithOtherType' => [
'<?php
function a(int $a, ?int $b = null): void
{
if ($a === $b) {
throw new InvalidArgumentException(sprintf("a can not be the same as b (b: %s).", $b));
}
}'
],
];
}

Expand Down

0 comments on commit f62b83a

Please sign in to comment.