Skip to content

Commit

Permalink
Manually intersect
Browse files Browse the repository at this point in the history
  • Loading branch information
danog committed Jul 25, 2022
1 parent bcc9d10 commit 3c2d86d
Showing 1 changed file with 3 additions and 46 deletions.
49 changes: 3 additions & 46 deletions src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php
Expand Up @@ -798,7 +798,7 @@ public static function applyAssertionsToContext(
}
} elseif (isset($context->vars_in_scope[$assertion_var_id])) {
$other_type = $context->vars_in_scope[$assertion_var_id];
$union = self::createUnionIntersectionFromOldType($union, $other_type);
$union = self::createUnionIntersectionFromOldType($union, $other_type, $codebase);

if ($union !== null) {
foreach ($union->getAtomicTypes() as $atomic_type) {
Expand Down Expand Up @@ -1136,58 +1136,15 @@ public static function checkTemplateResult(
}
}

/**
* This method should detect if the new type narrows down the old type.
*/
private static function isNewTypeNarrowingDownOldType(Union $new_type, Union $old_type): bool
{
if (count($new_type->getAtomicTypes()) === 1) {
return true;
}

// non-mixed is always better than mixed
if ($old_type->isMixed() && !$new_type->hasMixed()) {
return true;
}

// non-nullable is always better than nullable
if ($old_type->isNullable() && !$new_type->isNullable()) {
return true;
}

// Do not hassle around with non-single old types if they are not nullable
if (count($old_type->getAtomicTypes()) !== 1) {
return false;
}

// Do not hassle around with single literals as they supposed to be more accurate than any new type assertion
if ($old_type->isSingleFloatLiteral()
|| $old_type->isSingleIntLiteral()
|| $old_type->isSingleStringLiteral()
) {
return false;
}

// Literals should always replace non-literals
if (($old_type->isString() && $new_type->allStringLiterals())
|| ($old_type->isInt() && $new_type->allIntLiterals())
|| ($old_type->isFloat() && $new_type->allFloatLiterals())
) {
return true;
}

return false;
}

/**
* This method should kick all literals within `new_type` which are not part of the already known `old_type`.
* So lets say we already know that the old type is one of "a", "b" or "c".
* If another assertion takes place to determine if the value is either "a", "c" or "d", we can kick "d" as that
* won't be possible.
*/
private static function createUnionIntersectionFromOldType(Union $new_type, Union $old_type): ?Union
private static function createUnionIntersectionFromOldType(Union $new_type, Union $old_type, Codebase $codebase): ?Union
{
if (!self::isNewTypeNarrowingDownOldType($new_type, $old_type)) {
if (count($old_type->getAtomicTypes()) === count(Type::intersectUnionTypes($new_type, $old_type, $codebase)->getAtomicTypes())) {
return null;
}

Expand Down

0 comments on commit 3c2d86d

Please sign in to comment.