|
11 | 11 | use Psalm\Internal\Analyzer\ClassLikeAnalyzer; |
12 | 12 | use Psalm\Internal\Analyzer\FunctionLikeAnalyzer; |
13 | 13 | use Psalm\Internal\Analyzer\NamespaceAnalyzer; |
| 14 | +use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodCallReturnTypeFetcher; |
14 | 15 | use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodVisibilityAnalyzer; |
15 | 16 | use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer; |
16 | 17 | use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; |
|
21 | 22 | use Psalm\Internal\MethodIdentifier; |
22 | 23 | use Psalm\Internal\Type\TemplateResult; |
23 | 24 | use Psalm\Internal\Type\TemplateStandinTypeReplacer; |
| 25 | +use Psalm\Internal\Type\TypeExpander; |
24 | 26 | use Psalm\Issue\AbstractInstantiation; |
25 | 27 | use Psalm\Issue\DeprecatedClass; |
26 | 28 | use Psalm\Issue\ImpureMethodCall; |
|
58 | 60 |
|
59 | 61 | use function array_map; |
60 | 62 | use function array_values; |
| 63 | +use function count; |
61 | 64 | use function in_array; |
62 | 65 | use function md5; |
63 | 66 | use function preg_match; |
@@ -429,6 +432,8 @@ private static function analyzeNamedConstructor( |
429 | 432 |
|
430 | 433 | $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id); |
431 | 434 |
|
| 435 | + $method_storage = null; |
| 436 | + |
432 | 437 | if ($declaring_method_id) { |
433 | 438 | $method_storage = $codebase->methods->getStorage($declaring_method_id); |
434 | 439 |
|
@@ -500,6 +505,7 @@ private static function analyzeNamedConstructor( |
500 | 505 | } |
501 | 506 |
|
502 | 507 | $generic_param_types = null; |
| 508 | + $self_out_candidate = null; |
503 | 509 |
|
504 | 510 | if ($storage->template_types) { |
505 | 511 | foreach ($storage->template_types as $template_name => $base_type) { |
@@ -537,9 +543,49 @@ private static function analyzeNamedConstructor( |
537 | 543 | 'had_template' => true, |
538 | 544 | ]); |
539 | 545 | } |
| 546 | + |
| 547 | + if ($method_storage && $method_storage->self_out_type) { |
| 548 | + $self_out_candidate = $method_storage->self_out_type; |
| 549 | + |
| 550 | + if ($template_result->lower_bounds) { |
| 551 | + $self_out_candidate = TypeExpander::expandUnion( |
| 552 | + $codebase, |
| 553 | + $self_out_candidate, |
| 554 | + $fq_class_name, |
| 555 | + null, |
| 556 | + $storage->parent_class, |
| 557 | + true, |
| 558 | + false, |
| 559 | + false, |
| 560 | + true, |
| 561 | + ); |
| 562 | + } |
| 563 | + |
| 564 | + $self_out_candidate = MethodCallReturnTypeFetcher::replaceTemplateTypes( |
| 565 | + $self_out_candidate, |
| 566 | + $template_result, |
| 567 | + $method_id, |
| 568 | + count($stmt->getArgs()), |
| 569 | + $codebase, |
| 570 | + ); |
| 571 | + |
| 572 | + $self_out_candidate = TypeExpander::expandUnion( |
| 573 | + $codebase, |
| 574 | + $self_out_candidate, |
| 575 | + $fq_class_name, |
| 576 | + $fq_class_name, |
| 577 | + $storage->parent_class, |
| 578 | + true, |
| 579 | + false, |
| 580 | + false, |
| 581 | + true, |
| 582 | + ); |
| 583 | + $statements_analyzer->node_data->setType($stmt, $self_out_candidate); |
| 584 | + } |
540 | 585 | } |
541 | 586 |
|
542 | | - if ($generic_param_types) { |
| 587 | + // XXX: what if we need both? |
| 588 | + if ($generic_param_types && !$self_out_candidate) { |
543 | 589 | $result_atomic_type = new TGenericObject( |
544 | 590 | $fq_class_name, |
545 | 591 | $generic_param_types, |
|
0 commit comments