mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
parent
f66af3e267
commit
ca765cba58
@ -430,6 +430,7 @@ class NewAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\CallAna
|
||||
if (is_string($extended_template_name)
|
||||
&& $extended_atomic_type instanceof Type\Atomic\TTemplateParam
|
||||
&& $extended_atomic_type->param_name === $template_name
|
||||
&& $extended_template_name !== $template_name
|
||||
) {
|
||||
return self::getGenericParamForOffset(
|
||||
$extended_template_name,
|
||||
|
@ -1391,6 +1391,10 @@ class TypeAnalyzer
|
||||
array_keys($input_class_storage->template_types)
|
||||
);
|
||||
|
||||
if (!isset($input_type_params[$old_params_offset])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$candidate_param_type = $input_type_params[$old_params_offset];
|
||||
} else {
|
||||
$candidate_param_type = new Type\Union([$et]);
|
||||
@ -1506,6 +1510,10 @@ class TypeAnalyzer
|
||||
}
|
||||
|
||||
foreach ($input_type_part->type_params as $i => $input_param) {
|
||||
if ($i > 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
$container_param = $container_type_part->type_params[$i];
|
||||
|
||||
if ($i === 0
|
||||
|
@ -901,19 +901,6 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$template_type->isSingle()) {
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidDocblock(
|
||||
'Template type cannot be a union in docblock for '
|
||||
. implode('.', $this->fq_classlike_names),
|
||||
new CodeLocation($this->file_scanner, $node, null, true)
|
||||
)
|
||||
)) {
|
||||
$storage->has_docblock_issues = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$storage->template_types[$template_name] = [
|
||||
$template_type,
|
||||
$fq_classlike_name
|
||||
@ -1779,20 +1766,6 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
|
||||
$template_type = Type::getMixed();
|
||||
}
|
||||
|
||||
if (!$template_type->isSingle()) {
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidDocblock(
|
||||
'Template type cannot be a union in docblock for '
|
||||
. implode('.', $this->fq_classlike_names),
|
||||
new CodeLocation($this->file_scanner, $stmt, null, true)
|
||||
)
|
||||
)) {
|
||||
$storage->has_docblock_issues = true;
|
||||
}
|
||||
|
||||
$template_type = Type::getMixed();
|
||||
}
|
||||
} else {
|
||||
$template_type = Type::getMixed();
|
||||
}
|
||||
|
@ -1527,6 +1527,55 @@ class TemplateExtendsTest extends TestCase
|
||||
use T;
|
||||
}',
|
||||
],
|
||||
'extendWithTooFewArgs' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template TKey of array-key
|
||||
* @template T
|
||||
* @template-extends IteratorAggregate<TKey, T>
|
||||
*/
|
||||
interface Collection extends IteratorAggregate
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MissingTemplateParam
|
||||
* @template T
|
||||
* @template TKey of array-key
|
||||
* @template-implements Collection<TKey>
|
||||
*/
|
||||
class ArrayCollection implements Collection
|
||||
{
|
||||
/**
|
||||
* @psalm-var T[]
|
||||
*/
|
||||
private $elements;
|
||||
|
||||
/**
|
||||
* @psalm-param array<T> $elements
|
||||
*/
|
||||
public function __construct(array $elements = [])
|
||||
{
|
||||
$this->elements = $elements;
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MissingTemplateParam
|
||||
*
|
||||
* @psalm-param array<T> $elements
|
||||
* @psalm-return ArrayCollection<T>
|
||||
*/
|
||||
protected function createFrom(array $elements)
|
||||
{
|
||||
return new static($elements);
|
||||
}
|
||||
}',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -1863,6 +1863,79 @@ class TemplateTest extends TestCase
|
||||
return new CustomReflectionClass($className);
|
||||
}'
|
||||
],
|
||||
'ignoreTooManyArrayArgs' => [
|
||||
'<?php
|
||||
|
||||
function takesArray(array $arr) : void {}
|
||||
|
||||
/**
|
||||
* @psalm-suppress TooManyTemplateParams
|
||||
* @var array<int, int, int>
|
||||
*/
|
||||
$b = [1, 2, 3];
|
||||
takesArray($b);'
|
||||
],
|
||||
'ignoreTooManyGenericObjectArgs' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class C {
|
||||
/** @var T */
|
||||
public $t;
|
||||
|
||||
/** @param T $t */
|
||||
public function __construct($t) {
|
||||
$this->t = $t;
|
||||
}
|
||||
}
|
||||
|
||||
/** @param C<int> $c */
|
||||
function takesC(C $c) : void {}
|
||||
|
||||
/**
|
||||
* @psalm-suppress TooManyTemplateParams
|
||||
* @var C<int, int>
|
||||
*/
|
||||
$c = new C(5);
|
||||
takesC($c);'
|
||||
],
|
||||
'classTemplateUnionType' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template T0 as int|string
|
||||
*/
|
||||
class C {
|
||||
/**
|
||||
* @param T0 $t
|
||||
*/
|
||||
public function foo($t) : void {}
|
||||
}
|
||||
|
||||
/** @param C<int> $c */
|
||||
function foo(C $c) : void {}
|
||||
|
||||
/** @param C<string> $c */
|
||||
function bar(C $c) : void {}',
|
||||
],
|
||||
'functionTemplateUnionType' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template T0 as int|string
|
||||
* @param T0 $t
|
||||
* @return T0
|
||||
*/
|
||||
function foo($t) {
|
||||
return $t;
|
||||
}
|
||||
|
||||
$s = foo("hello");
|
||||
$i = foo(5);',
|
||||
'assertions' => [
|
||||
'$s' => 'string',
|
||||
'$i' => 'int',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -2322,22 +2395,6 @@ class TemplateTest extends TestCase
|
||||
$templated_list->add(5, []);',
|
||||
'error_message' => 'InvalidArgument',
|
||||
],
|
||||
'classTemplateUnionType' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template T0 as int|string
|
||||
*/
|
||||
class Foo {}',
|
||||
'error_message' => 'InvalidDocblock'
|
||||
],
|
||||
'functionTemplateUnionType' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template T0 as int|string
|
||||
*/
|
||||
function foo() : void {}',
|
||||
'error_message' => 'InvalidDocblock'
|
||||
],
|
||||
'copyScopedClassInFunction' => [
|
||||
'<?php
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user