1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-12 09:19:40 +01:00
psalm/src/Psalm/Type.php

724 lines
21 KiB
PHP
Raw Normal View History

2016-06-14 07:23:57 +02:00
<?php
2016-07-26 00:37:44 +02:00
namespace Psalm;
2016-06-14 07:23:57 +02:00
2016-11-21 05:31:10 +01:00
use Psalm\Exception\TypeParseTreeException;
2016-07-26 00:37:44 +02:00
use Psalm\Type\Atomic;
use Psalm\Type\Atomic\Generic;
use Psalm\Type\Atomic\ObjectLike;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TBool;
use Psalm\Type\Atomic\TEmpty;
use Psalm\Type\Atomic\TFalse;
use Psalm\Type\Atomic\TFloat;
use Psalm\Type\Atomic\TGenericObject;
use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TResource;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTrue;
use Psalm\Type\Atomic\TVoid;
2016-07-26 00:37:44 +02:00
use Psalm\Type\ParseTree;
2017-06-29 17:18:02 +02:00
use Psalm\Type\TypeCombination;
2016-11-02 07:29:00 +01:00
use Psalm\Type\Union;
2016-06-14 07:23:57 +02:00
abstract class Type
{
/**
* Parses a string type representation
2016-11-02 07:29:00 +01:00
*
* @param string $type_string
2017-05-27 02:16:18 +02:00
*
2016-06-28 20:28:45 +02:00
* @return Union
2016-06-14 07:23:57 +02:00
*/
2016-06-28 20:28:45 +02:00
public static function parseString($type_string)
2016-06-14 07:23:57 +02:00
{
2016-11-21 05:31:10 +01:00
// remove all unacceptable characters
2016-11-21 16:29:59 +01:00
$type_string = preg_replace('/[^A-Za-z0-9_\\\\|\? \<\>\{\}:,\]\[\(\)\$]/', '', trim($type_string));
2016-11-21 05:31:10 +01:00
2016-06-27 04:02:23 +02:00
if (strpos($type_string, '[') !== false) {
$type_string = self::convertSquareBrackets($type_string);
2016-06-27 04:02:23 +02:00
}
2017-11-20 06:37:45 +01:00
$type_string = preg_replace('/\?(?=[a-zA-Z])/', 'null|', $type_string);
if (preg_match('/[\[\]()\?]/', $type_string)) {
2017-10-12 20:02:06 +02:00
throw new TypeParseTreeException('Invalid characters in type');
}
$type_tokens = self::tokenize($type_string);
2016-06-14 07:23:57 +02:00
if (count($type_tokens) === 1) {
$type_tokens[0] = self::fixScalarTerms($type_tokens[0]);
2016-06-24 00:45:46 +02:00
return new Union([Atomic::create($type_tokens[0])]);
2016-06-14 07:23:57 +02:00
}
2016-11-21 05:31:10 +01:00
try {
$parse_tree = ParseTree::createFromTokens($type_tokens);
2017-01-15 16:58:44 +01:00
$parsed_type = self::getTypeFromTree($parse_tree);
} catch (TypeParseTreeException $e) {
2016-11-21 05:31:10 +01:00
throw $e;
2017-01-15 16:58:44 +01:00
} catch (\Exception $e) {
throw new TypeParseTreeException($e->getMessage());
2016-11-21 05:31:10 +01:00
}
2016-06-14 07:23:57 +02:00
2016-06-28 20:28:45 +02:00
if (!($parsed_type instanceof Union)) {
2016-06-15 01:22:29 +02:00
$parsed_type = new Union([$parsed_type]);
}
return $parsed_type;
2016-06-14 07:23:57 +02:00
}
2016-10-15 06:12:57 +02:00
/**
* @param string $type_string
2017-05-27 02:16:18 +02:00
*
2016-10-15 06:12:57 +02:00
* @return string
*/
public static function fixScalarTerms($type_string)
{
2016-08-14 18:07:19 +02:00
if (in_array(
strtolower($type_string),
2016-11-02 07:29:00 +01:00
[
'numeric',
'int',
'void',
2016-11-02 07:29:00 +01:00
'float',
'string',
'bool',
'true',
'false',
'null',
'array',
'object',
'mixed',
'resource',
2017-01-09 07:48:55 +01:00
'callable',
'iterable',
2017-06-13 06:51:16 +02:00
],
true
2016-08-14 18:07:19 +02:00
)) {
return strtolower($type_string);
2016-11-02 07:29:00 +01:00
} elseif ($type_string === 'boolean') {
return 'bool';
2016-11-02 07:29:00 +01:00
} elseif ($type_string === 'integer') {
return 'int';
2016-11-02 07:29:00 +01:00
} elseif ($type_string === 'double' || $type_string === 'real') {
2016-08-14 18:07:19 +02:00
return 'float';
}
return $type_string;
}
2016-11-02 07:29:00 +01:00
/**
* @param ParseTree $parse_tree
2017-05-27 02:16:18 +02:00
*
* @return Atomic|TArray|TGenericObject|ObjectLike|Union
2016-11-02 07:29:00 +01:00
*/
2016-06-14 07:23:57 +02:00
private static function getTypeFromTree(ParseTree $parse_tree)
{
2016-10-30 17:46:18 +01:00
if (!$parse_tree->value) {
throw new \InvalidArgumentException('Parse tree must have a value');
}
2016-06-14 07:23:57 +02:00
if ($parse_tree->value === ParseTree::GENERIC) {
$generic_type = array_shift($parse_tree->children);
$generic_params = array_map(
2016-12-07 20:13:39 +01:00
/**
* @return Union
*/
2016-06-14 07:23:57 +02:00
function (ParseTree $child_tree) {
$tree_type = self::getTypeFromTree($child_tree);
2017-05-25 04:07:49 +02:00
return $tree_type instanceof Union ? $tree_type : new Union([$tree_type]);
2016-06-14 07:23:57 +02:00
},
$parse_tree->children
);
2016-10-30 17:46:18 +01:00
if (!$generic_type->value) {
throw new \InvalidArgumentException('Generic type must have a value');
}
2016-09-09 22:21:49 +02:00
$generic_type_value = self::fixScalarTerms($generic_type->value);
2016-11-02 07:29:00 +01:00
if (($generic_type_value === 'array' || $generic_type_value === 'Generator') &&
count($generic_params) === 1
) {
array_unshift($generic_params, new Union([new TMixed]));
2016-09-09 22:21:49 +02:00
}
2016-06-14 07:23:57 +02:00
if (!$generic_params) {
throw new \InvalidArgumentException('No generic params provided for type');
}
2016-09-22 04:15:46 +02:00
if ($generic_type_value === 'array') {
return new TArray($generic_params);
2016-09-22 04:15:46 +02:00
}
return new TGenericObject($generic_type_value, $generic_params);
2016-06-14 07:23:57 +02:00
}
if ($parse_tree->value === ParseTree::UNION) {
$union_types = array_map(
2016-12-07 20:13:39 +01:00
/**
* @return Atomic
*/
2016-06-14 07:23:57 +02:00
function (ParseTree $child_tree) {
2016-12-07 20:13:39 +01:00
$atomic_type = self::getTypeFromTree($child_tree);
if (!$atomic_type instanceof Atomic) {
throw new \UnexpectedValueException(
'Was expecting an atomic type, got ' . get_class($atomic_type)
);
}
return $atomic_type;
2016-06-14 07:23:57 +02:00
},
$parse_tree->children
);
2016-11-20 08:52:34 +01:00
return self::combineTypes($union_types);
2016-06-14 07:23:57 +02:00
}
if ($parse_tree->value === ParseTree::OBJECT_LIKE) {
$properties = [];
$type = array_shift($parse_tree->children);
foreach ($parse_tree->children as $i => $property_branch) {
if ($property_branch->value !== ParseTree::OBJECT_PROPERTY) {
$property_type = self::getTypeFromTree($property_branch);
$property_key = (string)$i;
} elseif (count($property_branch->children) === 2) {
$property_type = self::getTypeFromTree($property_branch->children[1]);
$property_key = (string)($property_branch->children[0]->value);
} else {
throw new \InvalidArgumentException('Unexpected number of property parts');
}
2016-10-03 00:59:16 +02:00
if (!$property_type instanceof Union) {
$property_type = new Union([$property_type]);
}
$properties[$property_key] = $property_type;
}
if ($type->value !== 'array') {
throw new \InvalidArgumentException('Object-like type must be array');
2016-10-30 17:46:18 +01:00
}
return new ObjectLike($properties);
}
2016-09-09 22:21:49 +02:00
$atomic_type = self::fixScalarTerms($parse_tree->value);
return Atomic::create($atomic_type);
}
/**
2016-10-30 17:46:18 +01:00
* @param string $return_type
* @param bool $ignore_space
2017-05-27 02:16:18 +02:00
*
2016-10-03 17:38:59 +02:00
* @return array<int,string>
*/
public static function tokenize($return_type, $ignore_space = true)
{
$return_type_tokens = [''];
$was_char = false;
if ($ignore_space) {
$return_type = str_replace(' ', '', $return_type);
}
foreach (str_split($return_type) as $char) {
if ($was_char) {
$return_type_tokens[] = '';
}
2016-11-02 07:29:00 +01:00
if ($char === '<' ||
$char === '>' ||
$char === '|' ||
$char === '?' ||
$char === ',' ||
$char === '{' ||
$char === '}' ||
$char === '[' ||
$char === ']' ||
$char === ' ' ||
2016-11-02 07:29:00 +01:00
$char === ':'
) {
if ($return_type_tokens[count($return_type_tokens) - 1] === '') {
$return_type_tokens[count($return_type_tokens) - 1] = $char;
2016-11-02 07:29:00 +01:00
} else {
$return_type_tokens[] = $char;
}
$was_char = true;
2016-11-02 07:29:00 +01:00
} else {
$return_type_tokens[count($return_type_tokens) - 1] .= $char;
$was_char = false;
}
}
return $return_type_tokens;
}
2016-10-30 17:46:18 +01:00
/**
* @param string $type
2017-05-27 02:16:18 +02:00
*
2016-10-30 17:46:18 +01:00
* @return string
*/
public static function convertSquareBrackets($type)
{
$class_chars = '[a-zA-Z0-9\<\>\\\\_]+';
2017-05-25 04:07:49 +02:00
return preg_replace_callback(
2016-11-20 08:52:34 +01:00
'/(' . $class_chars . '|' . '\((' . $class_chars . '(\|' . $class_chars . ')*' . ')\))((\[\])+)/',
2016-12-07 20:13:39 +01:00
/**
* @return string
*/
function (array $matches) {
2016-10-30 17:46:18 +01:00
$inner_type = str_replace(['(', ')'], '', (string)$matches[1]);
2016-11-20 08:52:34 +01:00
$dimensionality = strlen((string)$matches[4]) / 2;
2017-05-27 02:05:57 +02:00
for ($i = 0; $i < $dimensionality; ++$i) {
2016-11-20 08:52:34 +01:00
$inner_type = 'array<mixed,' . $inner_type . '>';
}
return $inner_type;
},
$type
);
2016-06-14 07:23:57 +02:00
}
2016-06-15 01:22:29 +02:00
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getInt()
2016-06-15 01:22:29 +02:00
{
$type = new TInt;
2016-06-15 01:22:29 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-15 01:22:29 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getString()
2016-06-15 01:22:29 +02:00
{
$type = new TString;
2016-06-15 01:22:29 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-15 01:22:29 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getNull()
2016-06-16 02:16:40 +02:00
{
$type = new TNull;
2016-06-16 02:16:40 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-16 02:16:40 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getMixed()
2016-06-15 01:22:29 +02:00
{
$type = new TMixed;
2016-06-15 01:22:29 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-15 01:22:29 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getBool()
2016-06-15 01:22:29 +02:00
{
$type = new TBool;
2016-06-15 01:22:29 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-15 01:22:29 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getFloat()
2016-06-15 01:22:29 +02:00
{
$type = new TFloat;
2016-06-15 01:22:29 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-15 01:22:29 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getObject()
2016-06-15 01:22:29 +02:00
{
$type = new TObject;
2016-06-15 01:22:29 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-15 01:22:29 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-10-21 00:05:28 +02:00
public static function getClosure()
{
$type = new TNamedObject('Closure');
2016-10-21 00:05:28 +02:00
return new Union([$type]);
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getArray()
2016-06-15 01:22:29 +02:00
{
$type = new TArray(
2016-09-09 22:21:49 +02:00
[
new Type\Union([new TMixed]),
2017-05-27 02:05:57 +02:00
new Type\Union([new TMixed]),
2016-09-09 22:21:49 +02:00
]
);
2016-06-15 01:22:29 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-15 01:22:29 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-12 06:02:26 +02:00
public static function getEmptyArray()
{
return new Type\Union([
new TArray(
2016-09-12 06:02:26 +02:00
[
new Type\Union([new TEmpty]),
2017-05-27 02:05:57 +02:00
new Type\Union([new TEmpty]),
2016-09-12 06:02:26 +02:00
]
2017-05-27 02:05:57 +02:00
),
2016-09-12 06:02:26 +02:00
]);
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getVoid()
2016-06-16 02:16:40 +02:00
{
$type = new TVoid;
2016-06-16 02:16:40 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-16 02:16:40 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @return Type\Union
*/
2016-09-09 22:21:49 +02:00
public static function getFalse()
2016-06-16 02:16:40 +02:00
{
$type = new TFalse;
2016-06-16 02:16:40 +02:00
2016-09-09 22:21:49 +02:00
return new Union([$type]);
2016-06-16 02:16:40 +02:00
}
/**
* @return Type\Union
*/
public static function getTrue()
{
$type = new TTrue;
return new Union([$type]);
}
/**
* @return Type\Union
*/
public static function getResource()
{
return new Union([new TResource]);
}
/**
* @param array<string, Union> $redefined_vars
* @param Context $context
2017-05-27 02:16:18 +02:00
*
* @return void
*/
public static function redefineGenericUnionTypes(array $redefined_vars, Context $context)
{
foreach ($redefined_vars as $var_name => $redefined_union_type) {
foreach ($redefined_union_type->types as $redefined_atomic_type) {
foreach ($context->vars_in_scope[$var_name]->types as $context_type) {
if ($context_type instanceof Type\Atomic\TArray &&
$redefined_atomic_type instanceof Type\Atomic\TArray
) {
if ($context_type->type_params[1]->isEmpty()) {
$context_type->type_params[1] = $redefined_atomic_type->type_params[1];
2016-11-02 07:29:00 +01:00
} else {
$context_type->type_params[1] = Type::combineUnionTypes(
$redefined_atomic_type->type_params[1],
$context_type->type_params[1]
);
}
2016-09-09 22:21:49 +02:00
if ($context_type->type_params[0]->isEmpty()) {
$context_type->type_params[0] = $redefined_atomic_type->type_params[0];
} else {
$context_type->type_params[0] = Type::combineUnionTypes(
$redefined_atomic_type->type_params[0],
$context_type->type_params[0]
);
2016-09-09 22:21:49 +02:00
}
}
}
}
}
}
2016-06-16 02:16:40 +02:00
/**
* Combines two union types into one
2016-11-02 07:29:00 +01:00
*
2016-06-16 02:16:40 +02:00
* @param Union $type_1
* @param Union $type_2
2017-05-27 02:16:18 +02:00
*
2016-06-16 02:16:40 +02:00
* @return Union
*/
public static function combineUnionTypes(Union $type_1, Union $type_2)
2016-06-15 01:22:29 +02:00
{
2017-03-13 23:06:56 +01:00
$both_failed_reconciliation = false;
if ($type_1->failed_reconciliation) {
if ($type_2->failed_reconciliation) {
$both_failed_reconciliation = true;
} else {
return $type_2;
}
} elseif ($type_2->failed_reconciliation) {
return $type_1;
}
$combined_type = self::combineTypes(array_merge(array_values($type_1->types), array_values($type_2->types)));
if (!$type_1->initialized || !$type_2->initialized) {
$combined_type->initialized = false;
}
if ($type_1->from_docblock || $type_2->from_docblock) {
$combined_type->from_docblock = true;
}
if ($type_1->ignore_nullable_issues || $type_2->ignore_nullable_issues) {
$combined_type->ignore_nullable_issues = true;
}
2017-03-13 23:06:56 +01:00
if ($both_failed_reconciliation) {
$combined_type->failed_reconciliation = true;
}
return $combined_type;
2016-06-16 02:16:40 +02:00
}
2016-06-15 01:22:29 +02:00
2016-06-16 02:16:40 +02:00
/**
* Combines types together
2016-11-02 07:29:00 +01:00
* - so `int + string = int|string`
* - so `array<int> + array<string> = array<int|string>`
* - and `array<int> + string = array<int>|string`
* - and `array<empty> + array<empty> = array<empty>`
* - and `array<string> + array<empty> = array<string>`
* - and `array + array<string> = array<mixed>`
2016-06-16 02:16:40 +02:00
*
* @param array<Atomic> $types
2017-05-27 02:16:18 +02:00
*
2016-06-16 02:16:40 +02:00
* @return Union
2016-12-25 02:08:58 +01:00
* @psalm-suppress TypeCoercion
2016-06-16 02:16:40 +02:00
*/
public static function combineTypes(array $types)
2016-06-16 02:16:40 +02:00
{
2017-05-27 02:05:57 +02:00
if (in_array(null, $types, true)) {
2016-06-16 02:16:40 +02:00
return Type::getMixed();
}
if (count($types) === 1) {
return new Union([$types[0]]);
}
if (!$types) {
throw new \InvalidArgumentException('You must pass at least one type to combineTypes');
}
2017-06-29 17:18:02 +02:00
$combination = new TypeCombination();
2016-06-16 02:16:40 +02:00
foreach ($types as $type) {
2017-06-29 17:18:02 +02:00
$result = self::scrapeTypeProperties($type, $combination);
2016-11-05 03:11:46 +01:00
if ($result) {
return $result;
2016-09-09 22:21:49 +02:00
}
2016-06-16 02:16:40 +02:00
}
if (count($combination->value_types) === 1
&& !count($combination->objectlike_entries)
&& !count($combination->type_params)
) {
if (isset($combination->value_types['false'])) {
return Type::getFalse();
}
if (isset($combination->value_types['true'])) {
return Type::getTrue();
2016-06-16 07:19:52 +02:00
}
2017-06-29 17:18:02 +02:00
} elseif (isset($combination->value_types['void'])) {
unset($combination->value_types['void']);
2016-11-13 05:59:31 +01:00
2017-06-29 17:18:02 +02:00
if (!isset($combination->value_types['null'])) {
$combination->value_types['null'] = new TNull();
2016-11-13 05:59:31 +01:00
}
2016-06-16 07:19:52 +02:00
}
2017-12-09 21:51:38 +01:00
if (isset($combination->value_types['true']) && isset($combination->value_types['false'])) {
unset($combination->value_types['true'], $combination->value_types['false']);
$combination->value_types['bool'] = new TBool();
}
2016-06-16 02:16:40 +02:00
$new_types = [];
if (count($combination->objectlike_entries) &&
(!isset($combination->type_params['array'])
|| $combination->type_params['array'][1]->isEmpty())
) {
$new_types[] = new ObjectLike($combination->objectlike_entries);
2016-09-10 00:36:35 +02:00
// if we're merging an empty array with an object-like, clobber empty array
unset($combination->type_params['array']);
}
2016-06-15 01:22:29 +02:00
foreach ($combination->type_params as $generic_type => $generic_type_params) {
if ($generic_type === 'array') {
if ($combination->objectlike_entries) {
$object_like_generic_type = null;
$objectlike_keys = [];
foreach ($combination->objectlike_entries as $property_name => $property_type) {
if ($object_like_generic_type) {
$object_like_generic_type = Type::combineUnionTypes(
$property_type,
$object_like_generic_type
);
} else {
$object_like_generic_type = $property_type;
}
if (is_int($property_name)) {
if (!isset($objectlike_keys['int'])) {
$objectlike_keys['int'] = new TInt;
}
} else {
if (!isset($objectlike_keys['string'])) {
$objectlike_keys['string'] = new TString;
}
}
}
if (!$object_like_generic_type) {
throw new \InvalidArgumentException('Cannot be null');
}
$objectlike_key_type = new Type\Union(array_values($objectlike_keys));
$generic_type_params[0] = Type::combineUnionTypes(
$generic_type_params[0],
$objectlike_key_type
);
$generic_type_params[1] = Type::combineUnionTypes(
$generic_type_params[1],
$object_like_generic_type
);
}
$new_types[] = new TArray($generic_type_params);
} elseif (!isset($combination->value_types[$generic_type])) {
$new_types[] = new TGenericObject($generic_type, $generic_type_params);
}
2016-06-15 01:22:29 +02:00
}
foreach ($combination->value_types as $type) {
if (!($type instanceof TEmpty)
|| (count($combination->value_types) === 1
&& !count($new_types))
) {
$new_types[] = $type;
}
}
2016-06-16 02:16:40 +02:00
$new_types = array_values($new_types);
2016-06-16 02:16:40 +02:00
return new Union($new_types);
2016-06-15 01:22:29 +02:00
}
2016-11-05 03:11:46 +01:00
/**
2017-06-29 17:18:02 +02:00
* @param Atomic $type
* @param TypeCombination $combination
2017-05-27 02:16:18 +02:00
*
2016-11-05 03:11:46 +01:00
* @return null|Union
*/
2017-06-29 17:18:02 +02:00
public static function scrapeTypeProperties(Atomic $type, TypeCombination $combination)
2016-11-05 03:11:46 +01:00
{
if ($type instanceof TMixed) {
2016-11-05 03:11:46 +01:00
return Type::getMixed();
}
// deal with false|bool => bool
if (($type instanceof TFalse || $type instanceof TTrue) && isset($combination->value_types['bool'])) {
2016-11-05 03:11:46 +01:00
return null;
}
2017-12-09 21:51:38 +01:00
if (get_class($type) === 'Psalm\\Type\\Atomic\\TBool' && isset($combination->value_types['false'])) {
2017-06-29 17:18:02 +02:00
unset($combination->value_types['false']);
2016-11-05 03:11:46 +01:00
}
2017-12-09 21:51:38 +01:00
if (get_class($type) === 'Psalm\\Type\\Atomic\\TBool' && isset($combination->value_types['true'])) {
unset($combination->value_types['true']);
}
$type_key = $type->getKey();
if ($type instanceof TArray || $type instanceof TGenericObject) {
for ($i = 0; $i < count($type->type_params); ++$i) {
$type_param = $type->type_params[$i];
2016-11-05 03:11:46 +01:00
if (isset($combination->type_params[$type_key][$i])) {
$combination->type_params[$type_key][$i] = Type::combineUnionTypes(
$combination->type_params[$type_key][$i],
$type_param
);
} else {
$combination->type_params[$type_key][$i] = $type_param;
}
2016-11-05 03:11:46 +01:00
}
} elseif ($type instanceof ObjectLike) {
foreach ($type->properties as $candidate_property_name => $candidate_property_type) {
$value_type = isset($combination->objectlike_entries[$candidate_property_name])
? $combination->objectlike_entries[$candidate_property_name]
: null;
if (!$value_type) {
$combination->objectlike_entries[$candidate_property_name] = $candidate_property_type;
2016-11-05 03:11:46 +01:00
} else {
$combination->objectlike_entries[$candidate_property_name] = Type::combineUnionTypes(
$value_type,
2016-11-05 03:11:46 +01:00
$candidate_property_type
);
}
}
} else {
$combination->value_types[$type_key] = $type;
2016-11-05 03:11:46 +01:00
}
}
2016-06-14 07:23:57 +02:00
}