1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Remove false from template param as necessary

Fixes #3737
This commit is contained in:
Brown 2020-07-03 01:07:50 -04:00
parent ba63ccb825
commit 6419788a49
4 changed files with 108 additions and 25 deletions

View File

@ -714,11 +714,7 @@ class AssertionFinder
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
$false_type = Type::getFalse();
if (!TypeAnalyzer::isContainedBy(
$codebase,
$var_type,
$false_type
) && !TypeAnalyzer::isContainedBy(
if (!TypeAnalyzer::canExpressionTypesBeIdentical(
$codebase,
$false_type,
$var_type

View File

@ -127,19 +127,27 @@ class NegatedAssertionReconciler extends Reconciler
$existing_var_atomic_types = $existing_var_type->getAtomicTypes();
$simple_negated_type = SimpleNegatedAssertionReconciler::reconcile(
$assertion,
$existing_var_type,
$key,
$code_location,
$suppressed_issues,
$failed_reconciliation,
$is_equality,
$is_strict_equality
);
if ($assertion === 'false' && isset($existing_var_atomic_types['bool'])) {
$existing_var_type->removeType('bool');
$existing_var_type->addType(new TTrue);
} elseif ($assertion === 'true' && isset($existing_var_atomic_types['bool'])) {
$existing_var_type->removeType('bool');
$existing_var_type->addType(new TFalse);
} else {
$simple_negated_type = SimpleNegatedAssertionReconciler::reconcile(
$assertion,
$existing_var_type,
$key,
$code_location,
$suppressed_issues,
$failed_reconciliation,
$is_equality,
$is_strict_equality
);
if ($simple_negated_type) {
return $simple_negated_type;
if ($simple_negated_type) {
return $simple_negated_type;
}
}
if ($assertion === 'iterable' || $assertion === 'countable') {
@ -164,14 +172,6 @@ class NegatedAssertionReconciler extends Reconciler
return $existing_var_type;
}
if ($assertion === 'false' && isset($existing_var_atomic_types['bool'])) {
$existing_var_type->removeType('bool');
$existing_var_type->addType(new TTrue);
} elseif ($assertion === 'true' && isset($existing_var_atomic_types['bool'])) {
$existing_var_type->removeType('bool');
$existing_var_type->addType(new TFalse);
}
if (strtolower($assertion) === 'traversable'
&& isset($existing_var_atomic_types['iterable'])
) {

View File

@ -158,6 +158,17 @@ class SimpleNegatedAssertionReconciler extends Reconciler
);
}
if ($assertion === 'false' && !$existing_var_type->hasMixed()) {
return self::reconcileFalse(
$existing_var_type,
$key,
$code_location,
$suppressed_issues,
$failed_reconciliation,
$is_equality
);
}
if ($assertion === 'non-empty-countable') {
return self::reconcileNonEmptyCountable(
$existing_var_type,
@ -410,6 +421,68 @@ class SimpleNegatedAssertionReconciler extends Reconciler
return Type::getMixed();
}
/**
* @param string[] $suppressed_issues
* @param 0|1|2 $failed_reconciliation
*/
private static function reconcileFalse(
Type\Union $existing_var_type,
?string $key,
?CodeLocation $code_location,
array $suppressed_issues,
int &$failed_reconciliation,
bool $is_equality
) : Type\Union {
$old_var_type_string = $existing_var_type->getId();
$did_remove_type = false;
if ($existing_var_type->hasType('false')) {
$did_remove_type = true;
$existing_var_type->removeType('false');
}
foreach ($existing_var_type->getAtomicTypes() as $type) {
if ($type instanceof TTemplateParam) {
$type->as = self::reconcileFalse(
$type->as,
null,
null,
$suppressed_issues,
$failed_reconciliation,
$is_equality
);
$existing_var_type->bustCache();
}
}
if (!$did_remove_type || empty($existing_var_type->getAtomicTypes())) {
if ($key && $code_location && !$is_equality) {
self::triggerIssueForImpossible(
$existing_var_type,
$old_var_type_string,
$key,
'!false',
!$did_remove_type,
$code_location,
$suppressed_issues
);
}
if (!$did_remove_type) {
$failed_reconciliation = 1;
}
}
if ($existing_var_type->getAtomicTypes()) {
return $existing_var_type;
}
$failed_reconciliation = 2;
return Type::getMixed();
}
/**
* @param string[] $suppressed_issues
* @param 0|1|2 $failed_reconciliation

View File

@ -1267,6 +1267,20 @@ class FunctionTemplateTest extends TestCase
return $a;
}'
],
'compareToFalse' => [
'<?php
/**
* @template T as int|false
* @param T $value
* @return int
*/
function foo($value) {
if ($value === false) {
return -1;
}
return $value;
}'
],
'refineTemplateTypeNotEmpty' => [
'<?php
/**