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

Add better typing rules for generic objects

This commit is contained in:
Matt Brown 2018-02-22 11:20:03 -05:00
parent a9852c3bc0
commit 1f7d95dde0
3 changed files with 59 additions and 4 deletions

View File

@ -11,6 +11,7 @@ use Psalm\Type\Atomic\TBool;
use Psalm\Type\Atomic\TCallable;
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;
@ -286,13 +287,49 @@ class TypeChecker
if ($input_type_part->shallowEquals($container_type_part) ||
(
$input_type_part instanceof TNamedObject &&
$container_type_part instanceof TNamedObject &&
self::isObjectContainedByObject($codebase, $input_type_part, $container_type_part)
$input_type_part instanceof TNamedObject
&& $container_type_part instanceof TNamedObject
&& self::isObjectContainedByObject($codebase, $input_type_part, $container_type_part)
)
) {
if ($container_type_part instanceof TGenericObject && !$input_type_part instanceof TGenericObject) {
$type_coerced = true;
$type_coerced_from_mixed = true;
return false;
}
$all_types_contain = true;
if ($input_type_part instanceof TGenericObject && $container_type_part instanceof TGenericObject) {
foreach ($input_type_part->type_params as $i => $input_param) {
if (!isset($container_type_part->type_params[$i])) {
$type_coerced = true;
$type_coerced_from_mixed = true;
$all_types_contain = false;
break;
}
$container_param = $container_type_part->type_params[$i];
if (!$input_param->isEmpty() &&
!self::isContainedBy(
$codebase,
$input_param,
$container_param,
$input_param->ignore_nullable_issues,
$input_param->ignore_falsable_issues,
$has_scalar_match,
$type_coerced,
$type_coerced_from_mixed
)
) {
$all_types_contain = false;
}
}
}
if (($input_type_part instanceof TArray || $input_type_part instanceof ObjectLike)
&& ($container_type_part instanceof TArray || $container_type_part instanceof ObjectLike)
) {

View File

@ -164,7 +164,7 @@ class ArrayObject implements IteratorAggregate, Traversable, ArrayAccess, Serial
/**
* Construct a new array object
* @link http://php.net/manual/en/arrayobject.construct.php
* @param array<Tkey, TValue>|object $input The input parameter accepts an array or an Object.
* @param array<TKey, TValue>|object $input The input parameter accepts an array or an Object.
* @param int $flags Flags to control the behaviour of the ArrayObject object.
* @param string $iterator_class Specify the class that will be used for iteration of the ArrayObject object. ArrayIterator is the default class used.
* @since 5.0.0

View File

@ -692,6 +692,24 @@ class ReturnTypeTest extends TestCase
}',
'error_message' => 'LessSpecificReturnStatement',
],
'moreSpecificGenericReturnType' => [
'<?php
/** @return Iterator<int, string> */
function foo(array $a) {
$obj = new ArrayObject($a);
return $obj->getIterator();
}',
'error_message' => 'LessSpecificReturnStatement',
],
'invalidGenericReturnType' => [
'<?php
/** @return ArrayIterator<int, string> */
function foo(array $a) {
$obj = new ArrayObject([1, 2, 3, 4]);
return $obj->getIterator();
}',
'error_message' => 'InvalidReturnStatement',
],
];
}
}