1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Ensure class template types are mapped to static methods where necessary

Ref #4733
This commit is contained in:
Matt Brown 2020-11-29 17:40:52 -05:00 committed by Daniil Gentili
parent 2840faa519
commit ad5ec9501d
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
8 changed files with 95 additions and 12 deletions

View File

@ -1071,7 +1071,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
$storage, $storage,
null, null,
new Type\Atomic\TNamedObject($fq_class_name), new Type\Atomic\TNamedObject($fq_class_name),
'$this' true
); );
$template_result = new \Psalm\Internal\Type\TemplateResult( $template_result = new \Psalm\Internal\Type\TemplateResult(

View File

@ -802,7 +802,7 @@ class ReturnTypeAnalyzer
$codebase->classlike_storage_provider->get($context->self), $codebase->classlike_storage_provider->get($context->self),
strtolower($function->name->name), strtolower($function->name->name),
new Type\Atomic\TNamedObject($context->self), new Type\Atomic\TNamedObject($context->self),
'$this' true
); );
$class_template_params = $class_template_params ?: []; $class_template_params = $class_template_params ?: [];

View File

@ -28,7 +28,7 @@ class ClassTemplateParamCollector
ClassLikeStorage $static_class_storage, ClassLikeStorage $static_class_storage,
?string $method_name = null, ?string $method_name = null,
?Type\Atomic $lhs_type_part = null, ?Type\Atomic $lhs_type_part = null,
?string $lhs_var_id = null bool $self_call = false
): ?array { ): ?array {
$static_fq_class_name = $static_class_storage->name; $static_fq_class_name = $static_class_storage->name;
@ -96,7 +96,7 @@ class ClassTemplateParamCollector
foreach ($static_class_storage->template_types as $type_name => $_) { foreach ($static_class_storage->template_types as $type_name => $_) {
if (isset($lhs_type_part->type_params[$i])) { if (isset($lhs_type_part->type_params[$i])) {
if ($lhs_var_id !== '$this' || $static_fq_class_name !== $static_class_storage->name) { if (!$self_call || $static_fq_class_name !== $static_class_storage->name) {
$class_template_params[$type_name][$static_class_storage->name] $class_template_params[$type_name][$static_class_storage->name]
= $lhs_type_part->type_params[$i]; = $lhs_type_part->type_params[$i];
} }
@ -183,13 +183,13 @@ class ClassTemplateParamCollector
} }
} }
if ($lhs_var_id !== '$this' || $static_fq_class_name !== $class_storage->name) { if (!$self_call || $static_fq_class_name !== $class_storage->name) {
$class_template_params[$type_name][$class_storage->name] $class_template_params[$type_name][$class_storage->name]
= $output_type_extends ?: Type::getMixed(); = $output_type_extends ?: Type::getMixed();
} }
} }
if (($lhs_var_id !== '$this' || $static_fq_class_name !== $class_storage->name) if ((!$self_call || $static_fq_class_name !== $class_storage->name)
&& !isset($class_template_params[$type_name]) && !isset($class_template_params[$type_name])
) { ) {
$class_template_params[$type_name] = [$class_storage->name => Type::getMixed()]; $class_template_params[$type_name] = [$class_storage->name => Type::getMixed()];
@ -216,7 +216,7 @@ class ClassTemplateParamCollector
} }
} }
if ($lhs_var_id !== '$this') { if (!$self_call) {
if (!isset($class_template_params[$type_name])) { if (!isset($class_template_params[$type_name])) {
$class_template_params[$type_name][$class_storage->name] = $type; $class_template_params[$type_name][$class_storage->name] = $type;
} }

View File

@ -730,7 +730,7 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
$codebase->classlike_storage_provider->get($fq_class_name), $codebase->classlike_storage_provider->get($fq_class_name),
null, null,
$lhs_type_part, $lhs_type_part,
$lhs_var_id $lhs_var_id === '$this'
); );
$lhs_type_part = clone $mixin; $lhs_type_part = clone $mixin;

View File

@ -145,7 +145,7 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
$class_storage, $class_storage,
$method_name_lc, $method_name_lc,
$lhs_type_part, $lhs_type_part,
$lhs_var_id $lhs_var_id === '$this'
); );
if ($lhs_var_id === '$this' && $parent_source instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer) { if ($lhs_var_id === '$this' && $parent_source instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer) {
@ -167,7 +167,7 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
$class_storage, $class_storage,
$method_name_lc, $method_name_lc,
$lhs_type_part, $lhs_type_part,
$lhs_var_id $lhs_var_id === '$this'
); );
} }
} }

View File

@ -130,7 +130,7 @@ class ExistingAtomicStaticCallAnalyzer
$class_storage, $class_storage,
$method_name_lc, $method_name_lc,
$lhs_type_part, $lhs_type_part,
null !$statements_analyzer->isStatic() && $method_id->fq_class_name === $context->self
); );
if ($found_generic_params if ($found_generic_params

View File

@ -259,7 +259,7 @@ class ReturnAnalyzer
$class_storage, $class_storage,
strtolower($method_name), strtolower($method_name),
null, null,
'$this' true
); );
if ($found_generic_params) { if ($found_generic_params) {

View File

@ -3105,6 +3105,89 @@ class ClassTemplateTest extends TestCase
takesFMixed($f); takesFMixed($f);
}' }'
], ],
'arrayCollectionMapInternal' => [
'<?php
/**
* @psalm-template TKey of array-key
* @psalm-template T
* @psalm-consistent-constructor
*/
class ArrayCollection
{
/** @psalm-var array<TKey,T> */
private $elements;
/** @psalm-param array<TKey,T> $elements */
public function __construct(array $elements = [])
{
$this->elements = $elements;
}
/**
* @template TNewKey of array-key
* @template TNew
* @psalm-param array<TNewKey, TNew> $elements
* @psalm-return static<TNewKey, TNew>
*/
protected static function createFrom(array $elements)
{
return new static($elements);
}
/**
* @psalm-template U
* @psalm-param Closure(T=):U $func
* @psalm-return static<TKey, U>
*/
public function map(Closure $func)
{
$new_elements = array_map($func, $this->elements);
return self::createFrom($new_elements);
}
}'
],
'arrayCollectionMapExternal' => [
'<?php
/**
* @psalm-template TKey of array-key
* @psalm-template T
* @psalm-consistent-constructor
*/
class ArrayCollection
{
/** @psalm-var array<TKey,T> */
private $elements;
/** @psalm-param array<TKey,T> $elements */
public function __construct(array $elements = [])
{
$this->elements = $elements;
}
/**
* @psalm-template U
* @psalm-param Closure(T=):U $func
* @psalm-return ArrayCollection<TKey, U>
*/
public function map(Closure $func)
{
$new_elements = array_map($func, $this->elements);
return Creator::createFrom($new_elements);
}
}
class Creator {
/**
* @template TNewKey of array-key
* @template TNew
* @psalm-param array<TNewKey, TNew> $elements
* @psalm-return ArrayCollection<TNewKey, TNew>
*/
public static function createFrom(array $elements) {
return new ArrayCollection($elements);
}
}'
],
]; ];
} }