Skip to content

Commit

Permalink
Merge pull request #8249 from someniatko/issue-8200
Browse files Browse the repository at this point in the history
Improve inferring the "final" `static` type when calling static methods inside a different class
  • Loading branch information
AndrolGenhald committed Jul 14, 2022
2 parents 4fbbf82 + 1e0b572 commit cac9ec9
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
Expand Up @@ -551,6 +551,14 @@ private static function getMethodReturnType(
) {
$static_type = $context->self;
$context_final = $codebase->classlike_storage_provider->get($context->self)->final;
} elseif ($context->calling_method_id !== null) {
// differentiate between these cases:
// 1. "static" comes from the CALLED static method - use $fq_class_name.
// 2. "static" in return type comes from return type of the
// method CALLING the currently analyzed static method - use $context->self.
$static_type = self::hasStaticInType($return_type_candidate)
? $fq_class_name
: $context->self;
} else {
$static_type = $fq_class_name;
}
Expand Down Expand Up @@ -613,4 +621,22 @@ private static function getMethodReturnType(

return $return_type_candidate;
}

/**
* Dumb way to determine whether a type contains "static" somewhere inside.
*/
private static function hasStaticInType(Type\TypeNode $type): bool
{
if ($type instanceof TNamedObject && $type->value === 'static') {
return true;
}

foreach ($type->getChildNodes() as $child_type) {
if (self::hasStaticInType($child_type)) {
return true;
}
}

return false;
}
}
105 changes: 105 additions & 0 deletions tests/Template/ClassTemplateTest.php
Expand Up @@ -3694,6 +3694,111 @@ protected function setUp(): void
}
}',
],
'return TemplatedClass<static>' => [
'<?php
/**
* @template-covariant A
* @psalm-immutable
*/
final class Maybe
{
/**
* @param null|A $value
*/
public function __construct(private $value = null) {}
/**
* @template B
* @param B $value
* @return Maybe<B>
*
* @psalm-pure
*/
public static function just($value): self
{
return new self($value);
}
}
abstract class Test
{
final private function __construct() {}
/** @return Maybe<static> */
final public static function create(): Maybe
{
return Maybe::just(new static());
}
}',
],
'return list<static> created in a static method of another class' => [
'<?php
final class Lister
{
/**
* @template B
* @param B $value
* @return list<B>
*
* @psalm-pure
*/
public static function mklist($value): array
{
return [ $value ];
}
}
abstract class Test
{
final private function __construct() {}
/** @return list<static> */
final public static function create(): array
{
return Lister::mklist(new static());
}
}',
],
'use TemplatedClass<static> as an intermediate variable inside a method' => [
'<?php
/**
* @template-covariant A
* @psalm-immutable
*/
final class Maybe
{
/**
* @param A $value
*/
public function __construct(public $value) {}
/**
* @template B
* @param B $value
* @return Maybe<B>
*
* @psalm-pure
*/
public static function just($value): self
{
return new self($value);
}
}
abstract class Test
{
final private function __construct() {}
final public static function create(): static
{
$maybe = Maybe::just(new static());
return $maybe->value;
}
}',
],
];
}

Expand Down

0 comments on commit cac9ec9

Please sign in to comment.