Skip to content

Commit

Permalink
Merge pull request #10677 from robchett/template_union_object_incorre…
Browse files Browse the repository at this point in the history
…ct_assertions
  • Loading branch information
weirdan committed Feb 7, 2024
2 parents 06dbd4e + 7289f64 commit aedea60
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 14 deletions.
22 changes: 11 additions & 11 deletions src/Psalm/Internal/Type/SimpleAssertionReconciler.php
Original file line number Diff line number Diff line change
Expand Up @@ -1435,7 +1435,7 @@ private static function reconcileScalar(
if ($type instanceof Scalar) {
$scalar_types[] = $type;
} elseif ($type instanceof TTemplateParam) {
if ($type->as->hasScalar() || $type->as->hasMixed()) {
if ($type->as->hasScalarType() || $type->as->hasMixed()) {
$type = $type->replaceAs(self::reconcileScalar(
$assertion,
$type->as,
Expand Down Expand Up @@ -1526,7 +1526,7 @@ private static function reconcileNumeric(
$numeric_types[] = new TInt();
$numeric_types[] = new TNumericString();
} elseif ($type instanceof TTemplateParam) {
if ($type->as->hasNumeric() || $type->as->hasMixed()) {
if ($type->as->hasScalarType() || $type->as->hasMixed()) {
$type = $type->replaceAs(self::reconcileNumeric(
$assertion,
$type->as,
Expand Down Expand Up @@ -1605,14 +1605,6 @@ private static function reconcileObject(
/** @var TNamedObject|TTemplateParam|TIterable|TObjectWithProperties|TCallableObject $assertion_type */
$object_types[] = $type->addIntersectionType($assertion_type);
$redundant = false;
} elseif ($type->isObjectType()) {
if ($assertion_type_is_intersectable_type
&& !self::areIntersectionTypesAllowed($codebase, $type)
) {
$redundant = false;
} else {
$object_types[] = $type;
}
} elseif ($type instanceof TCallable) {
$callable_object = new TCallableObject($type->from_docblock, $type);
$object_types[] = $callable_object;
Expand All @@ -1624,7 +1616,7 @@ private static function reconcileObject(
$object_types[] = $type;
$redundant = false;
} elseif ($type instanceof TTemplateParam) {
if ($type->as->hasObject() || $type->as->hasMixed()) {
if ($type->as->hasObjectType() || $type->as->hasMixed()) {
/**
* @psalm-suppress PossiblyInvalidArgument This looks wrong, psalm assumes that $assertion_type
* can contain TNamedObject due to the reconciliation above
Expand All @@ -1650,6 +1642,14 @@ private static function reconcileObject(
}

$redundant = false;
} elseif ($type->isObjectType()) {
if ($assertion_type_is_intersectable_type
&& !self::areIntersectionTypesAllowed($codebase, $type)
) {
$redundant = false;
} else {
$object_types[] = $type;
}
} elseif ($type instanceof TIterable) {
$params = $type->type_params;
$params[0] = self::refineArrayKey($params[0]);
Expand Down
24 changes: 21 additions & 3 deletions src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php
Original file line number Diff line number Diff line change
Expand Up @@ -476,11 +476,29 @@ private static function reconcileBool(

foreach ($existing_var_type->getAtomicTypes() as $type) {
if ($type instanceof TTemplateParam) {
if (!$type->as->hasBool()) {
if (!$is_equality && !$type->as->isMixed()) {
$template_did_fail = 0;

$type = $type->replaceAs(self::reconcileBool(
$assertion,
$type->as,
null,
false,
null,
$suppressed_issues,
$template_did_fail,
$is_equality,
));

$redundant = false;

if (!$template_did_fail) {
$non_bool_types[] = $type;
}
} else {
$redundant = false;
$non_bool_types[] = $type;
}

$redundant = false;
} elseif (!$type instanceof TBool
|| ($is_equality && get_class($type) === TBool::class)
) {
Expand Down
23 changes: 23 additions & 0 deletions tests/Template/FunctionTemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,29 @@ function bar($foo): void {
if (!is_callable($foo)) {}
}',
],
'assertOnUnionTemplatedValue' => [
'code' => '<?php
/**
* @template I of bool|string|int|stdClass
* @param I $foo
*/
function bar($foo): void {
if (is_string($foo)) {}
if (!is_string($foo)) {}
if (is_int($foo)) {}
if (!is_int($foo)) {}
if (is_numeric($foo)) {}
if (!is_numeric($foo)) {}
if (is_scalar($foo)) {}
if (!is_scalar($foo)) {}
if (is_bool($foo)) {}
if (!is_bool($foo)) {}
if (is_object($foo)) {}
if (!is_object($foo)) {}
if (is_callable($foo)) {}
if (!is_callable($foo)) {}
}',
],
'interpretFunctionCallableReturnValue' => [
'code' => '<?php
final class Id
Expand Down

0 comments on commit aedea60

Please sign in to comment.