1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Fix #3603 - better typed value comparisons for loose equality

This commit is contained in:
Brown 2020-06-18 09:31:38 -04:00
parent 21e567832f
commit 137647a1a0
3 changed files with 85 additions and 18 deletions

View File

@ -970,8 +970,8 @@ class AssertionFinder
if ($var_name && $var_type) {
$identical = $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
|| ($other_type
&& (($var_type->isString() && $other_type->isString())
|| ($var_type->isInt() && $other_type->isInt())
&& (($var_type->isString(true) && $other_type->isString(true))
|| ($var_type->isInt(true) && $other_type->isInt(true))
|| ($var_type->isFloat() && $other_type->isFloat())
)
);

View File

@ -1535,13 +1535,20 @@ class Union implements TypeNode
/**
* @return bool true if this is an int
*/
public function isInt()
public function isInt(bool $check_templates = false)
{
if (!$this->isSingle()) {
return false;
}
return isset($this->types['int']) || $this->literal_int_types;
return count(
array_filter(
$this->types,
function ($type) use ($check_templates) {
return $type instanceof TInt
|| ($check_templates
&& $type instanceof TTemplateParam
&& $type->as->isInt()
);
}
)
) === count($this->types);
}
/**
@ -1559,17 +1566,20 @@ class Union implements TypeNode
/**
* @return bool true if this is a string
*/
public function isString()
public function isString(bool $check_templates = false)
{
if (!$this->isSingle()) {
return false;
}
return isset($this->types['string'])
|| isset($this->types['class-string'])
|| isset($this->types['trait-string'])
|| isset($this->types['numeric-string'])
|| $this->literal_string_types;
return count(
array_filter(
$this->types,
function ($type) use ($check_templates) {
return $type instanceof TString
|| ($check_templates
&& $type instanceof TTemplateParam
&& $type->as->isString()
);
}
)
) === count($this->types);
}
/**

View File

@ -2781,6 +2781,63 @@ class ClassTemplateTest extends TestCase
abstract protected function createMap(): Map;
}'
],
'looseEquality' => [
'<?php
/**
* @psalm-immutable
* @template T of self::READ_UNCOMMITTED|self::READ_COMMITTED|self::REPEATABLE_READ|self::SERIALIZABLE
*/
final class TransactionIsolationLevel
{
private const READ_UNCOMMITTED = "read uncommitted";
private const READ_COMMITTED = "read committed";
private const REPEATABLE_READ = "repeatable read";
private const SERIALIZABLE = "serializable";
/**
* @psalm-var T $level
*/
private string $level;
/**
* @psalm-param T $level
*/
private function __construct(string $level)
{
$this->level = $level;
}
/**
* @psalm-return self<self::READ_UNCOMMITTED>
*/
public static function readUncommitted(): self
{
return new self(self::READ_UNCOMMITTED);
}
/**
* @psalm-return T
*/
public function toString(): string
{
return $this->level;
}
/**
* @psalm-template TResult
* @psalm-param callable(self::READ_UNCOMMITTED): TResult $readUncommitted
* @psalm-return TResult
*/
public function resolve(callable $readUncommitted) {
if ($this->level == self::READ_UNCOMMITTED) {
return $readUncommitted($this->level);
}
throw new \LogicException("bad");
}
}'
],
];
}