1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Prevent extending covariant template params

Fixes #1610
This commit is contained in:
Brown 2019-05-14 18:51:30 -04:00
parent 7fbbe964cb
commit f1d8b1e6e7
3 changed files with 54 additions and 5 deletions

View File

@ -1517,15 +1517,42 @@ class ClassAnalyzer extends ClassLikeAnalyzer
} }
if ($parent_storage->template_types && $storage->template_type_extends) { if ($parent_storage->template_types && $storage->template_type_extends) {
$i = 0;
foreach ($parent_storage->template_types as $template_name => $type_map) { foreach ($parent_storage->template_types as $template_name => $type_map) {
foreach ($type_map as $template_type) { foreach ($type_map as $template_type) {
$parent_class_lc = strtolower($parent_storage->name); $parent_class_lc = strtolower($parent_storage->name);
if (!$template_type[0]->isMixed() if (isset($storage->template_type_extends[$parent_class_lc][$template_name])) {
&& isset($storage->template_type_extends[$parent_class_lc][$template_name])
) {
$extended_type = $storage->template_type_extends[$parent_class_lc][$template_name]; $extended_type = $storage->template_type_extends[$parent_class_lc][$template_name];
if (!TypeAnalyzer::isContainedBy($codebase, $extended_type, $template_type[0])) { if (isset($parent_storage->template_covariants[$i])
&& !$parent_storage->template_covariants[$i]
&& $parent_storage->user_defined
) {
foreach ($extended_type->getTypes() as $t) {
if ($t instanceof Type\Atomic\TTemplateParam
&& ($local_offset
= array_search($t->param_name, array_keys($storage->template_types)))
!== false
&& $storage->template_covariants[$local_offset]
) {
if (IssueBuffer::accepts(
new InvalidTemplateParam(
'Cannot extend an invariant template param ' . $template_name
. ' from an invariant context',
$code_location
),
array_merge($storage->suppressed_issues, $this->getSuppressedIssues())
)) {
// fall through
}
}
}
}
if (!$template_type[0]->isMixed()
&& !TypeAnalyzer::isContainedBy($codebase, $extended_type, $template_type[0])
) {
if (IssueBuffer::accepts( if (IssueBuffer::accepts(
new InvalidTemplateParam( new InvalidTemplateParam(
'Extended template param ' . $template_name 'Extended template param ' . $template_name
@ -1540,6 +1567,8 @@ class ClassAnalyzer extends ClassLikeAnalyzer
} }
} }
} }
$i++;
} }
} }
} }

View File

@ -370,7 +370,7 @@ abstract class Atomic
: false; : false;
if ($template_offset !== false if ($template_offset !== false
&& $class_storage->template_covariants && isset($class_storage->template_covariants[$template_offset])
&& $class_storage->template_covariants[$template_offset] && $class_storage->template_covariants[$template_offset]
) { ) {
if (IssueBuffer::accepts( if (IssueBuffer::accepts(

View File

@ -2575,6 +2575,26 @@ class TemplateExtendsTest extends TestCase
}', }',
'error_message' => 'MixedArgument - src/somefile.php:31:29 - Argument 1 of ord cannot be mixed, expecting string' 'error_message' => 'MixedArgument - src/somefile.php:31:29 - Argument 1 of ord cannot be mixed, expecting string'
], ],
'preventExtendingWithCovariance' => [
'<?php
/**
* @template T
*/
class InvariantFoo
{
/**
* @param T $value
*/
public function set($value): void {}
}
/**
* @template-covariant T
* @extends InvariantFoo<T>
*/
class CovariantFoo extends InvariantFoo {}',
'error_message' => 'InvalidTemplateParam'
],
]; ];
} }
} }