mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Fix issue reconciling class strings
This commit is contained in:
parent
f06ed8bf84
commit
d665f98fe7
@ -6,6 +6,7 @@ use Psalm\Storage\FunctionLikeParameter;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Atomic\HasClassString;
|
||||
use Psalm\Type\Atomic\ObjectLike;
|
||||
use Psalm\Type\Atomic\Scalar;
|
||||
use Psalm\Type\Atomic\TArray;
|
||||
@ -707,8 +708,8 @@ class TypeCombination
|
||||
if (!isset($combination->value_types['string'])) {
|
||||
if ($combination->strings) {
|
||||
$has_non_literal_class_string = false;
|
||||
foreach ($combination->strings as $literal_string) {
|
||||
if (!$literal_string instanceof TLiteralClassString) {
|
||||
foreach ($combination->strings as $string_type) {
|
||||
if (!$string_type instanceof TLiteralClassString) {
|
||||
$has_non_literal_class_string = true;
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,15 @@ use Psalm\Type\Union;
|
||||
/**
|
||||
* Represents a string whose value is a fully-qualified class found by get_class($var)
|
||||
*/
|
||||
class GetClassT extends T
|
||||
class GetClassT extends TString implements HasClassString
|
||||
{
|
||||
/**
|
||||
* Used to hold information as to what this refers to
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $typeof;
|
||||
|
||||
/**
|
||||
* @var Union
|
||||
*/
|
||||
@ -26,4 +33,28 @@ class GetClassT extends T
|
||||
{
|
||||
return 'class-string';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function canBeFullyExpressedInPhp()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasSingleNamedObject() : bool
|
||||
{
|
||||
return $this->as_type->isSingle() && $this->as_type->hasNamedObject();
|
||||
}
|
||||
|
||||
public function getSingleNamedObject() : TNamedObject
|
||||
{
|
||||
$first_value = array_values($this->as_type->getTypes())[0];
|
||||
|
||||
if (!$first_value instanceof TNamedObject) {
|
||||
throw new \UnexpectedValueException('Bad object');
|
||||
}
|
||||
|
||||
return $first_value;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,28 @@ namespace Psalm\Type\Atomic;
|
||||
/**
|
||||
* Represents a string whose value is that of a type found by gettype($var)
|
||||
*/
|
||||
class GetTypeT extends T
|
||||
class GetTypeT extends TString
|
||||
{
|
||||
/**
|
||||
* Used to hold information as to what this refers to
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $typeof;
|
||||
|
||||
/**
|
||||
* @param string $typeof the variable id
|
||||
*/
|
||||
public function __construct($typeof)
|
||||
{
|
||||
$this->typeof = $typeof;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function canBeFullyExpressedInPhp()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
10
src/Psalm/Type/Atomic/HasClassString.php
Normal file
10
src/Psalm/Type/Atomic/HasClassString.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Type\Atomic;
|
||||
|
||||
interface HasClassString
|
||||
{
|
||||
public function hasSingleNamedObject() : bool;
|
||||
|
||||
public function getSingleNamedObject() : TNamedObject;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
namespace Psalm\Type\Atomic;
|
||||
|
||||
/**
|
||||
* Represents a string that we know holds some type information about another variable,
|
||||
* specified in the $typeof property
|
||||
*/
|
||||
abstract class T extends TString
|
||||
{
|
||||
/**
|
||||
* Used to hold information as to what this refers to
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $typeof;
|
||||
|
||||
/**
|
||||
* @param string $typeof the variable id
|
||||
*/
|
||||
public function __construct($typeof)
|
||||
{
|
||||
$this->typeof = $typeof;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function canBeFullyExpressedInPhp()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace Psalm\Type\Atomic;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Union;
|
||||
|
||||
class TClassString extends TString
|
||||
class TClassString extends TString implements HasClassString
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@ -105,4 +105,18 @@ class TClassString extends TString
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasSingleNamedObject() : bool
|
||||
{
|
||||
return (bool) $this->as_type;
|
||||
}
|
||||
|
||||
public function getSingleNamedObject() : TNamedObject
|
||||
{
|
||||
if (!$this->as_type) {
|
||||
throw new \UnexpectedValueException('Bad object');
|
||||
}
|
||||
|
||||
return $this->as_type;
|
||||
}
|
||||
}
|
||||
|
@ -1790,8 +1790,6 @@ class Reconciler
|
||||
} elseif (substr($new_var_type, 0, 9) === 'getclass-') {
|
||||
$new_var_type = substr($new_var_type, 9);
|
||||
} elseif (!$is_equality) {
|
||||
$new_type_part = new TNamedObject($new_var_type);
|
||||
|
||||
$codebase = $statements_analyzer->getCodebase();
|
||||
|
||||
// if there wasn't a direct hit, go deeper, eliminating subtypes
|
||||
@ -1801,6 +1799,8 @@ class Reconciler
|
||||
continue;
|
||||
}
|
||||
|
||||
$new_type_part = new TNamedObject($new_var_type);
|
||||
|
||||
if (TypeAnalyzer::isAtomicContainedBy(
|
||||
$codebase,
|
||||
$existing_var_type_part,
|
||||
|
@ -438,11 +438,24 @@ class Union
|
||||
$this->id = null;
|
||||
|
||||
return true;
|
||||
} elseif ($type_string === 'string' && $this->literal_string_types) {
|
||||
foreach ($this->literal_string_types as $literal_key => $_) {
|
||||
unset($this->types[$literal_key]);
|
||||
}
|
||||
|
||||
if ($type_string === 'string') {
|
||||
if ($this->literal_string_types) {
|
||||
foreach ($this->literal_string_types as $literal_key => $_) {
|
||||
unset($this->types[$literal_key]);
|
||||
}
|
||||
$this->literal_string_types = [];
|
||||
}
|
||||
$this->literal_string_types = [];
|
||||
|
||||
if ($this->typed_class_strings) {
|
||||
foreach ($this->typed_class_strings as $typed_class_key => $_) {
|
||||
unset($this->types[$typed_class_key]);
|
||||
}
|
||||
$this->typed_class_strings = [];
|
||||
}
|
||||
|
||||
unset($this->types['class-string']);
|
||||
} elseif ($type_string === 'int' && $this->literal_int_types) {
|
||||
foreach ($this->literal_int_types as $literal_key => $_) {
|
||||
unset($this->types[$literal_key]);
|
||||
@ -506,6 +519,20 @@ class Union
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNamedObject()
|
||||
{
|
||||
foreach ($this->types as $type) {
|
||||
if ($type instanceof TNamedObject) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user