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:
parent
ceaea625f3
commit
def0489b98
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
if ($generic_param_types) {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: what if we need both?
|
||||
if ($generic_param_types && !$self_out_candidate) {
|
||||
$result_atomic_type = new TGenericObject(
|
||||
$fq_class_name,
|
||||
$generic_param_types,
|
||||
|
@ -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'>",
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user