1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-02 09:37:59 +01:00

Process @psalm-this-out on __construct() as well

Fixes vimeo/psalm#9649
This commit is contained in:
Bruce Weirdan 2024-02-11 01:47:08 +01:00
parent ceaea625f3
commit def0489b98
No known key found for this signature in database
GPG Key ID: CFC3AAB181751B0D
2 changed files with 67 additions and 1 deletions

View File

@ -11,6 +11,7 @@ use Psalm\Internal\Analyzer\ClassAnalyzer;
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodCallReturnTypeFetcher;
use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodVisibilityAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
@ -21,6 +22,7 @@ use Psalm\Internal\DataFlow\TaintSink;
use Psalm\Internal\MethodIdentifier;
use Psalm\Internal\Type\TemplateResult;
use Psalm\Internal\Type\TemplateStandinTypeReplacer;
use Psalm\Internal\Type\TypeExpander;
use Psalm\Issue\AbstractInstantiation;
use Psalm\Issue\DeprecatedClass;
use Psalm\Issue\ImpureMethodCall;
@ -58,6 +60,7 @@ use Psalm\Type\Union;
use function array_map;
use function array_values;
use function count;
use function in_array;
use function md5;
use function preg_match;
@ -429,6 +432,8 @@ final class NewAnalyzer extends CallAnalyzer
$declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
$method_storage = null;
if ($declaring_method_id) {
$method_storage = $codebase->methods->getStorage($declaring_method_id);
@ -500,6 +505,7 @@ final class NewAnalyzer extends CallAnalyzer
}
$generic_param_types = null;
$self_out_candidate = null;
if ($storage->template_types) {
foreach ($storage->template_types as $template_name => $base_type) {
@ -537,9 +543,49 @@ final class NewAnalyzer extends CallAnalyzer
'had_template' => true,
]);
}
if ($method_storage && $method_storage->self_out_type) {
$self_out_candidate = $method_storage->self_out_type;
if ($template_result->lower_bounds) {
$self_out_candidate = TypeExpander::expandUnion(
$codebase,
$self_out_candidate,
$fq_class_name,
null,
$storage->parent_class,
true,
false,
false,
true,
);
}
$self_out_candidate = MethodCallReturnTypeFetcher::replaceTemplateTypes(
$self_out_candidate,
$template_result,
$method_id,
count($stmt->getArgs()),
$codebase,
);
$self_out_candidate = TypeExpander::expandUnion(
$codebase,
$self_out_candidate,
$fq_class_name,
$fq_class_name,
$storage->parent_class,
true,
false,
false,
true,
);
$statements_analyzer->node_data->setType($stmt, $self_out_candidate);
}
}
if ($generic_param_types) {
// XXX: what if we need both?
if ($generic_param_types && !$self_out_candidate) {
$result_atomic_type = new TGenericObject(
$fq_class_name,
$generic_param_types,

View File

@ -84,6 +84,26 @@ class ThisOutTest extends TestCase
'$data3===' => 'list<2|3>',
],
],
'provideDefaultTypeToTypeArguments' => [
'code' => <<<'PHP'
<?php
/** @template T of 'idle'|'running' */
class App {
/** @psalm-this-out self<'idle'> */
public function __construct() {}
/**
* @psalm-if-this-is self<'idle'>
* @psalm-this-out self<'running'>
*/
public function start(): void {}
}
$app = new App();
PHP,
'assertions' => [
'$app===' => "App<'idle'>",
],
],
];
}
}