Skip to content

Commit

Permalink
Merge pull request #9883 from klimick/better-intersection-of-template…
Browse files Browse the repository at this point in the history
…-types-during-inheritance-check

Better intersection of template types during inheritance check
  • Loading branch information
orklah committed Jun 7, 2023
2 parents 4ebe4c1 + 4f5dfa7 commit 61e7a11
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 14 deletions.
20 changes: 20 additions & 0 deletions src/Psalm/Internal/Codebase/Methods.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Psalm\Codebase;
use Psalm\Context;
use Psalm\Internal\Analyzer\SourceAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Internal\MethodIdentifier;
Expand All @@ -18,6 +19,8 @@
use Psalm\Internal\Provider\MethodReturnTypeProvider;
use Psalm\Internal\Provider\MethodVisibilityProvider;
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
use Psalm\Internal\Type\TemplateResult;
use Psalm\Internal\Type\TypeExpander;
use Psalm\Internal\TypeVisitor\TypeLocalizer;
use Psalm\StatementsSource;
Expand Down Expand Up @@ -771,6 +774,23 @@ public function getMethodReturnType(
if ((!$old_contained_by_new && !$new_contained_by_old)
|| ($old_contained_by_new && $new_contained_by_old)
) {
$found_generic_params = ClassTemplateParamCollector::collect(
$source_analyzer->getCodebase(),
$appearing_fq_class_storage,
$appearing_fq_class_storage,
$appearing_method_name,
null,
true,
);

if ($found_generic_params) {
$overridden_storage_return_type = TemplateInferredTypeReplacer::replace(
$overridden_storage_return_type,
new TemplateResult([], $found_generic_params),
$source_analyzer->getCodebase(),
);
}

$attempted_intersection = null;
if ($old_contained_by_new) { //implicitly $new_contained_by_old as well
try {
Expand Down
14 changes: 0 additions & 14 deletions src/Psalm/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -739,20 +739,6 @@ public static function intersectUnionTypes(
$combined_type = null;
foreach ($type_1->getAtomicTypes() as $type_1_atomic) {
foreach ($type_2->getAtomicTypes() as $type_2_atomic) {
if ($type_1_atomic instanceof TTemplateParam
&& $type_2_atomic instanceof TNamedObject
) {
$intersected_with_template = self::intersectUnionTypes(
$type_1_atomic->as,
new Union([$type_2_atomic]),
$codebase,
);

if ($intersected_with_template && $intersected_with_template->isSingle()) {
$type_1_atomic = $intersected_with_template->getSingleAtomic();
}
}

$intersection_atomic = self::intersectAtomicTypes(
$type_1_atomic,
$type_2_atomic,
Expand Down
27 changes: 27 additions & 0 deletions tests/Template/ClassTemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4180,6 +4180,33 @@ function intWithNull(I $_type): void {}
intWithNull(new TWithNull(1));
intWithNull(new NullWithT(1));',
],
'intersectParentTemplateReturnWithConcreteChildReturn' => [
'code' => '<?php
/** @template T */
interface Aggregator
{
/**
* @psalm-param T ...$values
* @psalm-return T
*/
public function aggregate(...$values): mixed;
}
/** @implements Aggregator<int|float|null> */
final class AverageAggregator implements Aggregator
{
public function aggregate(...$values): null|int|float
{
if (!$values) {
return null;
}
return array_sum($values) / count($values);
}
}',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.0',
],
];
}

Expand Down

0 comments on commit 61e7a11

Please sign in to comment.