mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Overwrite memoized return type after sum-type candidate is calculated (#4805)
* Overwrite memoized return type after sum-type candidate is calculated * Fix mismatched types * Fix code style
This commit is contained in:
parent
7825a71351
commit
df8d2e2296
@ -79,4 +79,9 @@ class AtomicMethodCallAnalysisResult
|
|||||||
* @var list<\Psalm\Internal\MethodIdentifier>
|
* @var list<\Psalm\Internal\MethodIdentifier>
|
||||||
*/
|
*/
|
||||||
public $too_few_arguments_method_ids = [];
|
public $too_few_arguments_method_ids = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $can_memoize = false;
|
||||||
}
|
}
|
||||||
|
@ -200,8 +200,6 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
|
|||||||
|
|
||||||
$declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
|
$declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
|
||||||
|
|
||||||
$can_memoize = false;
|
|
||||||
|
|
||||||
$return_type_candidate = MethodCallReturnTypeFetcher::fetch(
|
$return_type_candidate = MethodCallReturnTypeFetcher::fetch(
|
||||||
$statements_analyzer,
|
$statements_analyzer,
|
||||||
$codebase,
|
$codebase,
|
||||||
@ -253,7 +251,7 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
|
|||||||
|
|
||||||
if ($method_storage) {
|
if ($method_storage) {
|
||||||
if (!$context->collect_mutations && !$context->collect_initializations) {
|
if (!$context->collect_mutations && !$context->collect_initializations) {
|
||||||
$can_memoize = MethodCallPurityAnalyzer::analyze(
|
$result->can_memoize = MethodCallPurityAnalyzer::analyze(
|
||||||
$statements_analyzer,
|
$statements_analyzer,
|
||||||
$codebase,
|
$codebase,
|
||||||
$stmt,
|
$stmt,
|
||||||
@ -349,23 +347,6 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$args && $lhs_var_id) {
|
|
||||||
if ($config->memoize_method_calls || $can_memoize) {
|
|
||||||
$method_var_id = $lhs_var_id . '->' . $method_name_lc . '()';
|
|
||||||
|
|
||||||
if (isset($context->vars_in_scope[$method_var_id])) {
|
|
||||||
$return_type_candidate = clone $context->vars_in_scope[$method_var_id];
|
|
||||||
|
|
||||||
if ($can_memoize) {
|
|
||||||
/** @psalm-suppress UndefinedPropertyAssignment */
|
|
||||||
$stmt->pure = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$context->vars_in_scope[$method_var_id] = $return_type_candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($codebase->methods_to_rename) {
|
if ($codebase->methods_to_rename) {
|
||||||
$declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
|
$declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ use Psalm\Type\Atomic\TNamedObject;
|
|||||||
use function count;
|
use function count;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
use function array_reduce;
|
use function array_reduce;
|
||||||
|
use function strtolower;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -201,6 +202,20 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
|||||||
$possible_new_class_types[] = $context->vars_in_scope[$lhs_var_id];
|
$possible_new_class_types[] = $context->vars_in_scope[$lhs_var_id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!$stmt->args && $lhs_var_id && $stmt->name instanceof PhpParser\Node\Identifier) {
|
||||||
|
if ($codebase->config->memoize_method_calls || $result->can_memoize) {
|
||||||
|
$method_var_id = $lhs_var_id . '->' . strtolower($stmt->name->name) . '()';
|
||||||
|
if (isset($context->vars_in_scope[$method_var_id])) {
|
||||||
|
$result->return_type = clone $context->vars_in_scope[$method_var_id];
|
||||||
|
if ($result->can_memoize) {
|
||||||
|
/** @psalm-suppress UndefinedPropertyAssignment */
|
||||||
|
$stmt->pure = true;
|
||||||
|
}
|
||||||
|
} elseif ($result->return_type !== null) {
|
||||||
|
$context->vars_in_scope[$method_var_id] = $result->return_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (count($possible_new_class_types) > 0) {
|
if (count($possible_new_class_types) > 0) {
|
||||||
$class_type = array_reduce(
|
$class_type = array_reduce(
|
||||||
|
@ -1476,6 +1476,43 @@ class ClassTemplateTest extends TestCase
|
|||||||
'$a_or_b' => 'A|B',
|
'$a_or_b' => 'A|B',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
'doNotCombineTypesWhenMemoized' => [
|
||||||
|
'<?php
|
||||||
|
class A {}
|
||||||
|
class B {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
*/
|
||||||
|
class C {
|
||||||
|
/**
|
||||||
|
* @var T
|
||||||
|
*/
|
||||||
|
private $t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param T $t
|
||||||
|
*/
|
||||||
|
public function __construct($t) {
|
||||||
|
$this->t = $t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return T
|
||||||
|
* @psalm-mutation-free
|
||||||
|
*/
|
||||||
|
public function get() {
|
||||||
|
return $this->t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var C<A>|C<B> $random_collection **/
|
||||||
|
$a_or_b = $random_collection->get();',
|
||||||
|
[
|
||||||
|
'$random_collection' => 'C<A>|C<B>',
|
||||||
|
'$a_or_b' => 'A|B',
|
||||||
|
],
|
||||||
|
],
|
||||||
'inferClosureParamTypeFromContext' => [
|
'inferClosureParamTypeFromContext' => [
|
||||||
'<?php
|
'<?php
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user