mirror of
https://github.com/danog/psalm.git
synced 2024-12-02 17:52:45 +01:00
Improve typing of properties
This commit is contained in:
parent
677614f23e
commit
6a9bcea901
@ -342,9 +342,21 @@ abstract class ClassLikeChecker implements StatementsSource
|
|||||||
$type_in_comment = CommentChecker::getTypeFromComment((string) $comment, null, $this);
|
$type_in_comment = CommentChecker::getTypeFromComment((string) $comment, null, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
$property_type = $type_in_comment ? $type_in_comment : Type::getMixed();
|
$property_group_type = $type_in_comment ? $type_in_comment : null;
|
||||||
|
|
||||||
foreach ($stmt->props as $property) {
|
foreach ($stmt->props as $property) {
|
||||||
|
if (!$property_group_type) {
|
||||||
|
if (!$property->default) {
|
||||||
|
$property_type = Type::getMixed();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$property_type = StatementsChecker::getSimpleType($property->default) ?: Type::getMixed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$property_type = $property_group_type;
|
||||||
|
}
|
||||||
|
|
||||||
if ($stmt->isStatic()) {
|
if ($stmt->isStatic()) {
|
||||||
if ($stmt->isPublic()) {
|
if ($stmt->isPublic()) {
|
||||||
self::$public_static_class_properties[$class_context->self][$property->name] = $property_type;
|
self::$public_static_class_properties[$class_context->self][$property->name] = $property_type;
|
||||||
@ -367,10 +379,6 @@ abstract class ClassLikeChecker implements StatementsSource
|
|||||||
self::$private_class_properties[$class_context->self][$property->name] = $property_type;
|
self::$private_class_properties[$class_context->self][$property->name] = $property_type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$stmt->isStatic()) {
|
|
||||||
$class_context->vars_in_scope['this->' . $property->name] = $property_type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif ($stmt instanceof PhpParser\Node\Stmt\ClassConst) {
|
elseif ($stmt instanceof PhpParser\Node\Stmt\ClassConst) {
|
||||||
@ -400,6 +408,16 @@ abstract class ClassLikeChecker implements StatementsSource
|
|||||||
(new StatementsChecker($this))->check($leftover_stmts, $class_context);
|
(new StatementsChecker($this))->check($leftover_stmts, $class_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$all_instance_properties = array_merge(
|
||||||
|
self::$public_class_properties[$this->absolute_class],
|
||||||
|
self::$protected_class_properties[$this->absolute_class],
|
||||||
|
self::$private_class_properties[$this->absolute_class]
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($all_instance_properties as $property_name => $property_type) {
|
||||||
|
$class_context->vars_in_scope['this->' . $property_name] = $property_type;
|
||||||
|
}
|
||||||
|
|
||||||
$config = Config::getInstance();
|
$config = Config::getInstance();
|
||||||
|
|
||||||
if ($check_methods) {
|
if ($check_methods) {
|
||||||
|
@ -1129,6 +1129,50 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getSimpleType(PhpParser\Node\Expr $stmt)
|
||||||
|
{
|
||||||
|
if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
||||||
|
// @todo support this
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch) {
|
||||||
|
// @todo support this as well
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Scalar\String_) {
|
||||||
|
return Type::getString();
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Scalar\LNumber) {
|
||||||
|
return Type::getInt();
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Scalar\DNumber) {
|
||||||
|
return Type::getFloat();
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Expr\Array_) {
|
||||||
|
return Type::getArray();
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Int_) {
|
||||||
|
return Type::getInt();
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Double) {
|
||||||
|
return Type::getFloat();
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Bool_) {
|
||||||
|
return Type::getBool();
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Expr\Cast\String_) {
|
||||||
|
return Type::getString();
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Object_) {
|
||||||
|
return Type::getObject();
|
||||||
|
}
|
||||||
|
elseif ($stmt instanceof PhpParser\Node\Expr\Cast\Array_) {
|
||||||
|
return Type::getArray();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var_dump('Unrecognised default property type in ' . $this->checked_file_name);
|
||||||
|
var_dump($stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param PhpParser\Node\Expr\Variable|PhpParser\Node\Expr\PropertyFetch $stmt
|
* @param PhpParser\Node\Expr\Variable|PhpParser\Node\Expr\PropertyFetch $stmt
|
||||||
* @param string $method_id
|
* @param string $method_id
|
||||||
@ -2232,29 +2276,36 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($nesting) {
|
if ($return_type) {
|
||||||
$context_type = clone $context->vars_in_scope[$var_id];
|
if ($nesting && $var_id) {
|
||||||
|
$context_type = clone $context->vars_in_scope[$var_id];
|
||||||
|
|
||||||
$array_type = $context_type;
|
$array_type = $context_type;
|
||||||
|
|
||||||
for ($i = 0; $i < $nesting + 1; $i++) {
|
for ($i = 0; $i < $nesting + 1; $i++) {
|
||||||
if ($i < $nesting) {
|
if ($array_type->isArray()) {
|
||||||
if ($array_type->types['array']->type_params[1]->isEmpty()) {
|
if ($i < $nesting) {
|
||||||
$array_type->types['array']->type_params[1] = $return_type;
|
if ($array_type->types['array']->type_params[1]->isEmpty()) {
|
||||||
break;
|
$array_type->types['array']->type_params[1] = $return_type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$array_type = $array_type->types['array']->type_params[1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$array_type->types['array']->type_params[1] = $return_type->types['array']->type_params[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$array_type = $array_type->types['array']->type_params[1];
|
$context->vars_in_scope[$var_id] = $context_type;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$array_type->types['array']->type_params[1] = $return_type->types['array']->type_params[1];
|
$context->vars_in_scope[$var_id] = $return_type;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$context->vars_in_scope[$var_id] = $context_type;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$context->vars_in_scope[$var_id] = $return_type;
|
$context->vars_in_scope[$var_id] = Type::getMixed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2289,10 +2340,15 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($type->isMixed()) {
|
||||||
|
// @todo emit issue
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($type->value !== 'array' && !ClassChecker::classImplements($type->value, 'ArrayAccess')) {
|
if ($type->value !== 'array' && !ClassChecker::classImplements($type->value, 'ArrayAccess')) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new InvalidArrayAssignment(
|
new InvalidArrayAssignment(
|
||||||
'Cannot assign value on variable ' . $var_id . ' that does not implement ArrayAccess',
|
'Cannot assign value on variable $' . $var_id . ' of type ' . $type->value . ' that does not implement ArrayAccess',
|
||||||
$this->checked_file_name,
|
$this->checked_file_name,
|
||||||
$line_number
|
$line_number
|
||||||
),
|
),
|
||||||
@ -3621,6 +3677,8 @@ class StatementsChecker
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$stmt->inferredType = Type::getString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerVariable($var_name, $line_number)
|
public function registerVariable($var_name, $line_number)
|
||||||
|
Loading…
Reference in New Issue
Block a user