Skip to content

Commit bbcf503

Browse files
authored
Merge pull request #10189 from boesing/feature/inherited-assertions-v2
Inherited assertions v2
2 parents e110305 + 56b719b commit bbcf503

File tree

3 files changed

+18
-110
lines changed

3 files changed

+18
-110
lines changed

src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@ public static function collect(
4848
if ($static_class_storage->template_extended_params
4949
&& $method_name
5050
&& !empty($non_trait_class_storage->overridden_method_ids[$method_name])
51-
&& isset($class_storage->methods[$method_name])
52-
&& (!isset($non_trait_class_storage->methods[$method_name]->return_type)
53-
|| $class_storage->methods[$method_name]->inherited_return_type)
5451
) {
5552
foreach ($non_trait_class_storage->overridden_method_ids[$method_name] as $overridden_method_id) {
5653
$overridden_storage = $codebase->methods->getStorage($overridden_method_id);

src/Psalm/Internal/Codebase/AssertionsFromInheritanceResolver.php

Lines changed: 1 addition & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,13 @@
55
namespace Psalm\Internal\Codebase;
66

77
use Psalm\Codebase;
8-
use Psalm\Storage\Assertion\IsType;
98
use Psalm\Storage\ClassLikeStorage;
109
use Psalm\Storage\MethodStorage;
1110
use Psalm\Storage\Possibilities;
12-
use Psalm\Type\Atomic\TTemplateParam;
1311

1412
use function array_filter;
15-
use function array_map;
1613
use function array_merge;
1714
use function array_values;
18-
use function reset;
1915
use function strtolower;
2016

2117
/**
@@ -61,80 +57,9 @@ public function resolve(
6157
* Since the inheritance does not provide its own assertions, we have to detect those
6258
* from inherited classes
6359
*/
64-
$assertions += array_map(
65-
fn(Possibilities $possibilities) => $this->modifyAssertionsForInheritance(
66-
$possibilities,
67-
$this->codebase,
68-
$called_class,
69-
$inherited_classes_and_interfaces,
70-
),
71-
$potential_assertion_providing_method_storage->assertions,
72-
);
60+
$assertions += $potential_assertion_providing_method_storage->assertions;
7361
}
7462

7563
return $assertions;
7664
}
77-
78-
/**
79-
* In case the called class is either implementing or extending a class/interface which does also has the
80-
* template we are searching for, we assume that the called method has the same assertions.
81-
*
82-
* @param list<class-string> $potential_assertion_providing_classes
83-
*/
84-
private function modifyAssertionsForInheritance(
85-
Possibilities $possibilities,
86-
Codebase $codebase,
87-
ClassLikeStorage $called_class,
88-
array $potential_assertion_providing_classes
89-
): Possibilities {
90-
$replacement = new Possibilities($possibilities->var_id, []);
91-
$extended_params = $called_class->template_extended_params;
92-
foreach ($possibilities->rule as $assertion) {
93-
if (!$assertion instanceof IsType
94-
|| !$assertion->type instanceof TTemplateParam) {
95-
$replacement->rule[] = $assertion;
96-
continue;
97-
}
98-
99-
/** Called class does not extend the template parameter */
100-
$extended_templates = $called_class->template_extended_params;
101-
if (!isset($extended_templates[$assertion->type->defining_class][$assertion->type->param_name])) {
102-
$replacement->rule[] = $assertion;
103-
continue;
104-
}
105-
106-
foreach ($potential_assertion_providing_classes as $potential_assertion_providing_class) {
107-
if (!isset($extended_params[$potential_assertion_providing_class][$assertion->type->param_name])) {
108-
continue;
109-
}
110-
111-
if (!$codebase->classlike_storage_provider->has($potential_assertion_providing_class)) {
112-
continue;
113-
}
114-
115-
$potential_assertion_providing_classlike_storage = $codebase->classlike_storage_provider->get(
116-
$potential_assertion_providing_class,
117-
);
118-
if (!isset(
119-
$potential_assertion_providing_classlike_storage->template_types[$assertion->type->param_name],
120-
)) {
121-
continue;
122-
}
123-
124-
$replacement->rule[] = new IsType(new TTemplateParam(
125-
$assertion->type->param_name,
126-
reset(
127-
$potential_assertion_providing_classlike_storage->template_types[$assertion->type->param_name],
128-
),
129-
$potential_assertion_providing_class,
130-
));
131-
132-
continue 2;
133-
}
134-
135-
$replacement->rule[] = $assertion;
136-
}
137-
138-
return $replacement;
139-
}
14065
}

tests/AssertAnnotationTest.php

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2890,9 +2890,6 @@ public static function doAssert($value): void
28902890
/** @template InstanceType */
28912891
interface PluginManagerInterface
28922892
{
2893-
/** @return InstanceType */
2894-
public function get(): mixed;
2895-
28962893
/** @psalm-assert InstanceType $value */
28972894
public function validate(mixed $value): void;
28982895
}
@@ -2903,15 +2900,6 @@ public function validate(mixed $value): void;
29032900
*/
29042901
abstract class AbstractPluginManager implements PluginManagerInterface
29052902
{
2906-
/** @param InstanceType $value */
2907-
public function __construct(private readonly mixed $value)
2908-
{}
2909-
2910-
/** {@inheritDoc} */
2911-
public function get(): mixed
2912-
{
2913-
return $this->value;
2914-
}
29152903
}
29162904
29172905
/**
@@ -2920,49 +2908,47 @@ public function get(): mixed
29202908
*/
29212909
abstract class AbstractSingleInstancePluginManager extends AbstractPluginManager
29222910
{
2923-
/**
2924-
* An object type that the created instance must be instanced of
2925-
*
2926-
* @var class-string<InstanceType>
2927-
*/
2928-
protected string $instanceOf;
2929-
2930-
/** {@inheritDoc} */
2931-
public function get(): object
2932-
{
2933-
return parent::get();
2934-
}
2935-
2936-
2937-
/** {@inheritDoc} */
29382911
public function validate(mixed $value): void
29392912
{
29402913
}
29412914
}
29422915
}
29432916
29442917
namespace Namespace2 {
2945-
use Namespace1\AbstractSingleInstancePluginManager;
2918+
use InvalidArgumentException;use Namespace1\AbstractSingleInstancePluginManager;
2919+
use Namespace1\AbstractPluginManager;
29462920
use stdClass;
29472921
29482922
/** @template-extends AbstractSingleInstancePluginManager<stdClass> */
29492923
final class Qoo extends AbstractSingleInstancePluginManager
29502924
{
2951-
/** @var class-string<stdClass> */
2952-
protected string $instanceOf = stdClass::class;
2925+
}
2926+
2927+
/** @template-extends AbstractPluginManager<callable> */
2928+
final class Ooq extends AbstractPluginManager
2929+
{
2930+
public function validate(mixed $value): void
2931+
{
2932+
}
29532933
}
29542934
}
29552935
29562936
namespace {
2957-
$baz = new \Namespace2\Qoo(new stdClass);
2937+
$baz = new \Namespace2\Qoo();
29582938
29592939
/** @var mixed $object */
29602940
$object = null;
29612941
$baz->validate($object);
2942+
2943+
$ooq = new \Namespace2\Ooq();
2944+
/** @var mixed $callable */
2945+
$callable = null;
2946+
$ooq->validate($callable);
29622947
}
29632948
',
29642949
'assertions' => [
29652950
'$object===' => 'stdClass',
2951+
'$callable===' => 'callable',
29662952
],
29672953
'ignored_issues' => [],
29682954
'php_version' => '8.1',

0 commit comments

Comments
 (0)