From ca765cba589598ea9b7fcdca4c6128060f5fa684 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sun, 17 Mar 2019 12:20:57 -0400 Subject: [PATCH] Fix errors caused by bad generic param counts Fixes #1474 --- .../Expression/Call/NewAnalyzer.php | 1 + src/Psalm/Internal/Analyzer/TypeAnalyzer.php | 8 ++ .../Internal/Visitor/ReflectorVisitor.php | 27 ------ tests/Template/TemplateExtendsTest.php | 49 ++++++++++ tests/Template/TemplateTest.php | 89 +++++++++++++++---- 5 files changed, 131 insertions(+), 43 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index 5c4f68b75..47604bcce 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -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, diff --git a/src/Psalm/Internal/Analyzer/TypeAnalyzer.php b/src/Psalm/Internal/Analyzer/TypeAnalyzer.php index b9772b430..55c3fa851 100644 --- a/src/Psalm/Internal/Analyzer/TypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/TypeAnalyzer.php @@ -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 diff --git a/src/Psalm/Internal/Visitor/ReflectorVisitor.php b/src/Psalm/Internal/Visitor/ReflectorVisitor.php index 93a7a0f1c..fa4074ebc 100644 --- a/src/Psalm/Internal/Visitor/ReflectorVisitor.php +++ b/src/Psalm/Internal/Visitor/ReflectorVisitor.php @@ -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(); } diff --git a/tests/Template/TemplateExtendsTest.php b/tests/Template/TemplateExtendsTest.php index 1dc90e1ce..d3b5f2674 100644 --- a/tests/Template/TemplateExtendsTest.php +++ b/tests/Template/TemplateExtendsTest.php @@ -1527,6 +1527,55 @@ class TemplateExtendsTest extends TestCase use T; }', ], + 'extendWithTooFewArgs' => [ + ' + */ + interface Collection extends IteratorAggregate + { + } + + /** + * @psalm-suppress MissingTemplateParam + * @template T + * @template TKey of array-key + * @template-implements Collection + */ + class ArrayCollection implements Collection + { + /** + * @psalm-var T[] + */ + private $elements; + + /** + * @psalm-param array $elements + */ + public function __construct(array $elements = []) + { + $this->elements = $elements; + } + + public function getIterator() + { + return new ArrayIterator($this->elements); + } + + /** + * @psalm-suppress MissingTemplateParam + * + * @psalm-param array $elements + * @psalm-return ArrayCollection + */ + protected function createFrom(array $elements) + { + return new static($elements); + } + }', + ], ]; } diff --git a/tests/Template/TemplateTest.php b/tests/Template/TemplateTest.php index 4ffd63816..4e18661c6 100644 --- a/tests/Template/TemplateTest.php +++ b/tests/Template/TemplateTest.php @@ -1863,6 +1863,79 @@ class TemplateTest extends TestCase return new CustomReflectionClass($className); }' ], + 'ignoreTooManyArrayArgs' => [ + ' + */ + $b = [1, 2, 3]; + takesArray($b);' + ], + 'ignoreTooManyGenericObjectArgs' => [ + 't = $t; + } + } + + /** @param C $c */ + function takesC(C $c) : void {} + + /** + * @psalm-suppress TooManyTemplateParams + * @var C + */ + $c = new C(5); + takesC($c);' + ], + 'classTemplateUnionType' => [ + ' $c */ + function foo(C $c) : void {} + + /** @param C $c */ + function bar(C $c) : void {}', + ], + 'functionTemplateUnionType' => [ + ' [ + '$s' => 'string', + '$i' => 'int', + ], + ], ]; } @@ -2322,22 +2395,6 @@ class TemplateTest extends TestCase $templated_list->add(5, []);', 'error_message' => 'InvalidArgument', ], - 'classTemplateUnionType' => [ - ' 'InvalidDocblock' - ], - 'functionTemplateUnionType' => [ - ' 'InvalidDocblock' - ], 'copyScopedClassInFunction' => [ '