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);
|
||||
}
|
||||
|
||||
$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) {
|
||||
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->isPublic()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$stmt->isStatic()) {
|
||||
$class_context->vars_in_scope['this->' . $property->name] = $property_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($stmt instanceof PhpParser\Node\Stmt\ClassConst) {
|
||||
@ -400,6 +408,16 @@ abstract class ClassLikeChecker implements StatementsSource
|
||||
(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();
|
||||
|
||||
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 string $method_id
|
||||
@ -2232,12 +2276,14 @@ class StatementsChecker
|
||||
}
|
||||
}
|
||||
|
||||
if ($nesting) {
|
||||
if ($return_type) {
|
||||
if ($nesting && $var_id) {
|
||||
$context_type = clone $context->vars_in_scope[$var_id];
|
||||
|
||||
$array_type = $context_type;
|
||||
|
||||
for ($i = 0; $i < $nesting + 1; $i++) {
|
||||
if ($array_type->isArray()) {
|
||||
if ($i < $nesting) {
|
||||
if ($array_type->types['array']->type_params[1]->isEmpty()) {
|
||||
$array_type->types['array']->type_params[1] = $return_type;
|
||||
@ -2250,6 +2296,7 @@ class StatementsChecker
|
||||
$array_type->types['array']->type_params[1] = $return_type->types['array']->type_params[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$context->vars_in_scope[$var_id] = $context_type;
|
||||
}
|
||||
@ -2257,6 +2304,10 @@ class StatementsChecker
|
||||
$context->vars_in_scope[$var_id] = $return_type;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$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 (IssueBuffer::accepts(
|
||||
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,
|
||||
$line_number
|
||||
),
|
||||
@ -3621,6 +3677,8 @@ class StatementsChecker
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt->inferredType = Type::getString();
|
||||
}
|
||||
|
||||
public function registerVariable($var_name, $line_number)
|
||||
|
Loading…
Reference in New Issue
Block a user