mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
parent
ba63ccb825
commit
6419788a49
@ -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
|
||||
|
@ -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'])
|
||||
) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user