1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Fix #1232 - allow checking of extended templated function returns

This commit is contained in:
Brown 2019-01-24 15:03:13 -05:00
parent 006801f661
commit 0d52dc4e00
10 changed files with 86 additions and 22 deletions

View File

@ -218,17 +218,20 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer implements Statements
}
}
MethodAnalyzer::compareMethods(
$codebase,
$class_storage,
$parent_storage,
$storage,
$parent_method_storage,
$fq_class_name,
$implementer_visibility,
$codeLocation,
$storage->suppressed_issues
);
// we've already checked this in the class checker
if (!isset($class_storage->class_implements[strtolower($overridden_fq_class_name)])) {
MethodAnalyzer::compareMethods(
$codebase,
$class_storage,
$parent_storage,
$storage,
$parent_method_storage,
$fq_class_name,
$implementer_visibility,
$codeLocation,
$storage->suppressed_issues
);
}
foreach ($parent_method_storage->params as $i => $guide_param) {
if ($guide_param->type

View File

@ -544,8 +544,10 @@ class MethodAnalyzer extends FunctionLikeAnalyzer
if ($guide_method_storage->return_type
&& $implementer_method_storage->return_type
&& ($guide_method_storage->signature_return_type !== $guide_method_storage->return_type
|| $implementer_method_storage->signature_return_type !== $implementer_method_storage->return_type)
&& $implementer_classlike_storage->user_defined
&& !$guide_classlike_storage->stubbed
&& (!$guide_classlike_storage->stubbed || $guide_classlike_storage->template_types)
) {
$implementer_method_storage_return_type = ExpressionAnalyzer::fleshOutType(
$codebase,

View File

@ -1226,9 +1226,16 @@ class TypeAnalyzer
}
if (!$input_type_part instanceof TGenericObject) {
$type_coerced = true;
$type_coerced_from_mixed = true;
return false;
if ($input_type_part instanceof TNamedObject) {
$input_type_part = new TGenericObject(
$input_type_part->value,
array_fill(0, count($container_type_part->type_params), Type::getMixed())
);
} else {
$type_coerced = true;
$type_coerced_from_mixed = true;
return false;
}
}
}

View File

@ -189,8 +189,8 @@ class Methods
*/
public function getMethodParams($method_id)
{
if ($method_id = $this->getDeclaringMethodId($method_id)) {
$storage = $this->getStorage($method_id);
if ($declaring_method_id = $this->getDeclaringMethodId($method_id)) {
$storage = $this->getStorage($declaring_method_id);
if ($storage->inheritdoc) {
$non_null_param_types = array_filter(
@ -216,7 +216,7 @@ class Methods
return $params;
}
$appearing_method_id = $this->getAppearingMethodId($method_id);
$appearing_method_id = $this->getAppearingMethodId($declaring_method_id);
if (!$appearing_method_id) {
return $params;

View File

@ -407,6 +407,8 @@ class Populator
$storage->pseudo_property_get_types += $parent_storage->pseudo_property_get_types;
$storage->pseudo_property_set_types += $parent_storage->pseudo_property_set_types;
$parent_storage->dependent_classlikes[strtolower($storage->name)] = true;
$storage->pseudo_methods += $parent_storage->pseudo_methods;
}
}
@ -503,8 +505,8 @@ class Populator
$implemented_interface_storage->invalid_dependencies
);
if ($implemented_interface_storage->template_types
&& isset($storage->template_type_extends[$implemented_interface_lc])
if (isset($storage->template_type_extends[$implemented_interface_lc])
&& $implemented_interface_storage->template_types
) {
foreach ($storage->template_type_extends[$implemented_interface_lc] as $i => $type) {
$parent_template_type_names = array_keys($implemented_interface_storage->template_types);
@ -524,16 +526,18 @@ class Populator
$interface_method_implementers = [];
foreach ($storage->class_implements as $implemented_interface) {
foreach ($storage->class_implements as $implemented_interface_lc => $_) {
try {
$implemented_interface = $this->classlikes->getUnAliasedName(
strtolower($implemented_interface)
$implemented_interface_lc
);
$implemented_interface_storage = $storage_provider->get($implemented_interface);
} catch (\InvalidArgumentException $e) {
continue;
}
$implemented_interface_storage->dependent_classlikes[strtolower($storage->name)] = true;
foreach ($implemented_interface_storage->methods as $method_name => $method) {
if ($method->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC) {
$mentioned_method_id = $implemented_interface . '::' . $method_name;

View File

@ -112,6 +112,14 @@ function array_flip(array $arr) {}
*/
function key($arr) {}
/**
* @psalm-template TValue
*
* @param TValue $value
* @return array<int, TValue>
*/
function array_fill( int $start_index, int $num, $value) : array {}
/**
* @psalm-template T
*

View File

@ -688,6 +688,13 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
// we're overwriting some methods
$storage = $duplicate_storage;
$this->codebase->classlike_storage_provider->makeNew(strtolower($fq_classlike_name));
$storage->populated = false;
foreach ($storage->dependent_classlikes as $dependent_name_lc => $_) {
$dependent_storage = $this->codebase->classlike_storage_provider->get($dependent_name_lc);
$dependent_storage->populated = false;
$this->codebase->classlike_storage_provider->makeNew($dependent_name_lc);
}
}
}
}

View File

@ -290,6 +290,11 @@ class ClassLikeStorage
*/
public $invalid_dependencies = [];
/**
* @var array<string, bool>
*/
public $dependent_classlikes = [];
/**
* A hash of the source file's name, contents, and this file's modified on date
*

View File

@ -388,6 +388,14 @@ class MethodSignatureTest extends TestCase
(new Y())->boo(new A());',
],
'allowMixedExtensionOfIteratorAggregate' => [
'<?php
class C implements IteratorAggregate {
public function getIterator(): Iterator {
return new ArrayIterator([]);
}
}'
],
];
}

View File

@ -2603,6 +2603,26 @@ class TemplateTest extends TestCase
echo foo("hello", []);',
'error_message' => 'PossiblyInvalidArgument',
],
'mismatchingTypesAfterExtends' => [
'<?php
class Foo {}
class Bar {}
/**
* @extends IteratorAggregate<int, Foo>
*/
class SomeIterator implements IteratorAggregate
{
/**
* @return Traversable<int, Bar>
*/
public function getIterator()
{
yield new Bar;
}
}',
'error_message' => 'ImplementedReturnTypeMismatch',
],
];
}
}