mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Improve robustness of template checks
This commit is contained in:
parent
4f9c040a15
commit
91686bef4b
@ -1597,6 +1597,14 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
if ($return_type && $class_storage->template_type_extends) {
|
||||
$generic_params = [];
|
||||
|
||||
$declaring_method_id = $codebase->methods->getDeclaringMethodId($analyzed_method_id);
|
||||
|
||||
if ($declaring_method_id) {
|
||||
$declaring_class_name = explode('::', $declaring_method_id)[0];
|
||||
|
||||
$class_storage = $codebase->classlike_storage_provider->get($declaring_class_name);
|
||||
}
|
||||
|
||||
$class_template_params = MethodCallAnalyzer::getClassTemplateParams(
|
||||
$codebase,
|
||||
$class_storage,
|
||||
|
@ -5,6 +5,7 @@ use PhpParser;
|
||||
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
|
||||
use Psalm\Internal\Analyzer\CommentAnalyzer;
|
||||
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Analyzer\TraitAnalyzer;
|
||||
use Psalm\Internal\Analyzer\TypeAnalyzer;
|
||||
@ -161,6 +162,31 @@ class ReturnAnalyzer
|
||||
|
||||
$local_return_type = $source->getLocalReturnType($storage->return_type);
|
||||
|
||||
if ($storage instanceof \Psalm\Storage\MethodStorage) {
|
||||
list($fq_class_name, $method_name) = explode('::', $cased_method_id);
|
||||
|
||||
if ($fq_class_name !== $context->self) {
|
||||
$class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
|
||||
|
||||
$found_generic_params = MethodCallAnalyzer::getClassTemplateParams(
|
||||
$codebase,
|
||||
$class_storage,
|
||||
$fq_class_name,
|
||||
strtolower($method_name),
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
if ($found_generic_params) {
|
||||
$local_return_type = clone $local_return_type;
|
||||
|
||||
$local_return_type->replaceTemplateTypesWithArgTypes(
|
||||
$found_generic_params
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($local_return_type->isGenerator() && $storage->has_yield) {
|
||||
return null;
|
||||
}
|
||||
|
@ -886,7 +886,7 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
public function valid(): bool { return false; }
|
||||
}',
|
||||
],
|
||||
'traitUse' => [
|
||||
'traitUseNotExtended' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template T
|
||||
@ -2195,6 +2195,37 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
}
|
||||
}'
|
||||
],
|
||||
'templatedInterfaceGetIteratorIteration' => [
|
||||
'<?php
|
||||
namespace NS;
|
||||
|
||||
/**
|
||||
* @template TKey
|
||||
* @template TValue
|
||||
* @template-extends \IteratorAggregate<TKey, TValue>
|
||||
*/
|
||||
interface ICollection extends \IteratorAggregate {
|
||||
/** @return \Traversable<TKey,TValue> */
|
||||
public function getIterator();
|
||||
}
|
||||
|
||||
class Collection implements ICollection {
|
||||
/** @var array */
|
||||
private $data;
|
||||
public function __construct(array $data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
/** @psalm-suppress LessSpecificImplementedReturnType */
|
||||
public function getIterator(): \Traversable {
|
||||
return new \ArrayIterator($this->data);
|
||||
}
|
||||
}
|
||||
|
||||
/** @var ICollection<string, int> */
|
||||
$c = new Collection(["a" => 1]);
|
||||
|
||||
foreach ($c->getIterator() as $k => $v) { atan($v); strlen($k); }',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ class ClassTemplateTest extends TestCase
|
||||
* @template T as object
|
||||
*/
|
||||
class Foo {
|
||||
/** @var T::class */
|
||||
/** @var class-string<T> */
|
||||
public $T;
|
||||
|
||||
/**
|
||||
@ -322,11 +322,11 @@ class ClassTemplateTest extends TestCase
|
||||
],
|
||||
'noRepeatedTypeException' => [
|
||||
'<?php
|
||||
/** @template T */
|
||||
/** @template T as object */
|
||||
class Foo
|
||||
{
|
||||
/**
|
||||
* @psalm-var class-string
|
||||
* @psalm-var class-string<T>
|
||||
*/
|
||||
private $type;
|
||||
|
||||
@ -334,14 +334,14 @@ class ClassTemplateTest extends TestCase
|
||||
private $items;
|
||||
|
||||
/**
|
||||
* @param class-string $type
|
||||
* @template-typeof T $type
|
||||
* @param class-string<T> $type
|
||||
*/
|
||||
public function __construct(string $type)
|
||||
{
|
||||
if (!in_array($type, [A::class, B::class], true)) {
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
$this->items = [];
|
||||
}
|
||||
@ -372,6 +372,7 @@ class ClassTemplateTest extends TestCase
|
||||
*/
|
||||
private function ensureFoo(array $items): Foo
|
||||
{
|
||||
/** @var class-string<T> */
|
||||
$type = $items[0] instanceof A ? A::class : B::class;
|
||||
return new Foo($type);
|
||||
}
|
||||
@ -441,36 +442,6 @@ class ClassTemplateTest extends TestCase
|
||||
|
||||
foreach ($c as $k => $v) { atan($v); strlen($k); }',
|
||||
],
|
||||
'templatedInterfaceGetIteratorIteration' => [
|
||||
'<?php
|
||||
namespace NS;
|
||||
|
||||
/**
|
||||
* @template TKey
|
||||
* @template TValue
|
||||
*/
|
||||
interface ICollection extends \IteratorAggregate {
|
||||
/** @return \Traversable<TKey,TValue> */
|
||||
public function getIterator();
|
||||
}
|
||||
|
||||
class Collection implements ICollection {
|
||||
/** @var array */
|
||||
private $data;
|
||||
public function __construct(array $data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
/** @psalm-suppress LessSpecificImplementedReturnType */
|
||||
public function getIterator(): \Traversable {
|
||||
return new \ArrayIterator($this->data);
|
||||
}
|
||||
}
|
||||
|
||||
/** @var ICollection<string, int> */
|
||||
$c = new Collection(["a" => 1]);
|
||||
|
||||
foreach ($c->getIterator() as $k => $v) { atan($v); strlen($k); }',
|
||||
],
|
||||
'allowTemplatedIntersectionToExtend' => [
|
||||
'<?php
|
||||
interface Foo {}
|
||||
@ -1172,7 +1143,7 @@ class ClassTemplateTest extends TestCase
|
||||
*
|
||||
* @param class-string<Q> $obj
|
||||
*
|
||||
* @return array<I, V>
|
||||
* @return array<I, V|Q>
|
||||
*/
|
||||
public function appendProperty(string $obj): array
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user