mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Support property type promotion ref #4089
This commit is contained in:
parent
7d5e9ef5f3
commit
613ce96582
@ -1854,6 +1854,8 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
|||||||
$fq_classlike_name = null;
|
$fq_classlike_name = null;
|
||||||
$is_functionlike_override = false;
|
$is_functionlike_override = false;
|
||||||
|
|
||||||
|
$method_name_lc = null;
|
||||||
|
|
||||||
if ($fake_method && $stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
|
if ($fake_method && $stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
|
||||||
$cased_function_id = '@method ' . $stmt->name->name;
|
$cased_function_id = '@method ' . $stmt->name->name;
|
||||||
|
|
||||||
@ -2359,6 +2361,61 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($class_storage && $method_name_lc === '__construct') {
|
||||||
|
foreach ($stmt->getParams() as $param) {
|
||||||
|
if (!$param->flags || !$param->var instanceof PhpParser\Node\Expr\Variable) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$param_storage = null;
|
||||||
|
|
||||||
|
foreach ($storage->params as $param_storage) {
|
||||||
|
if ($param_storage->name === $param->var->name) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$param_storage) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$property_storage = $class_storage->properties[$param_storage->name] = new PropertyStorage();
|
||||||
|
$property_storage->is_static = false;
|
||||||
|
$property_storage->type = $param_storage->type;
|
||||||
|
$property_storage->signature_type = $param_storage->signature_type;
|
||||||
|
$property_storage->signature_type_location = $param_storage->signature_type_location;
|
||||||
|
$property_storage->type_location = $param_storage->type_location;
|
||||||
|
$property_storage->location = $param_storage->location;
|
||||||
|
$property_storage->stmt_location = new CodeLocation($this->file_scanner, $param);
|
||||||
|
$property_storage->has_default = $param->default ? true : false;
|
||||||
|
|
||||||
|
$property_id = $fq_classlike_name . '::$' . $param_storage->name;
|
||||||
|
|
||||||
|
switch ($param->flags) {
|
||||||
|
case \PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC:
|
||||||
|
$property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PUBLIC;
|
||||||
|
$class_storage->inheritable_property_ids[$param_storage->name] = $property_id;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case \PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED:
|
||||||
|
$property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PROTECTED;
|
||||||
|
$class_storage->inheritable_property_ids[$param_storage->name] = $property_id;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case \PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE:
|
||||||
|
$property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PRIVATE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fq_classlike_name = $this->fq_classlike_names[count($this->fq_classlike_names) - 1];
|
||||||
|
|
||||||
|
$property_id = $fq_classlike_name . '::$' . $param_storage->name;
|
||||||
|
|
||||||
|
$class_storage->declaring_property_ids[$param_storage->name] = $fq_classlike_name;
|
||||||
|
$class_storage->appearing_property_ids[$param_storage->name] = $property_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod
|
if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod
|
||||||
&& $stmt->name->name === '__construct'
|
&& $stmt->name->name === '__construct'
|
||||||
&& $class_storage
|
&& $class_storage
|
||||||
|
@ -2074,6 +2074,34 @@ class PropertyTypeTest extends TestCase
|
|||||||
}
|
}
|
||||||
}'
|
}'
|
||||||
],
|
],
|
||||||
|
'promotedPublicProperty' => [
|
||||||
|
'<?php
|
||||||
|
class A {
|
||||||
|
public function __construct(public int $foo = 5) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo (new A)->foo;'
|
||||||
|
],
|
||||||
|
'promotedProtectedProperty' => [
|
||||||
|
'<?php
|
||||||
|
class A {
|
||||||
|
public function __construct(protected int $foo = 5) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AChild extends A {
|
||||||
|
public function bar() : int {
|
||||||
|
return $this->foo;
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
],
|
||||||
|
'setPublicProperty' => [
|
||||||
|
'<?php
|
||||||
|
class A {
|
||||||
|
public function __construct(public int $foo) {
|
||||||
|
$this->foo = 5;
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3214,6 +3242,22 @@ class PropertyTypeTest extends TestCase
|
|||||||
}',
|
}',
|
||||||
'error_message' => 'MissingPropertyType',
|
'error_message' => 'MissingPropertyType',
|
||||||
],
|
],
|
||||||
|
'promotedPrivateProperty' => [
|
||||||
|
'<?php
|
||||||
|
class A {
|
||||||
|
public function __construct(private int $foo = 5) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo (new A)->foo;',
|
||||||
|
'error_message' => 'InaccessibleProperty',
|
||||||
|
],
|
||||||
|
'promotedPublicPropertyWithoutSet' => [
|
||||||
|
'<?php
|
||||||
|
class A {
|
||||||
|
public function __construct(public int $foo) {}
|
||||||
|
}',
|
||||||
|
'error_message' => 'PropertyNotSetInConstructor'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user