1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Emit issue if property type is missing

This commit is contained in:
Matthew Brown 2016-10-31 15:17:54 -04:00
parent e8fa46149e
commit c2f63c392e
4 changed files with 91 additions and 22 deletions

View File

@ -3,6 +3,7 @@
namespace Psalm\Checker;
use Psalm\Issue\InvalidClass;
use Psalm\Issue\MissingPropertyType;
use Psalm\Issue\UndefinedClass;
use Psalm\Issue\UndefinedTrait;
use Psalm\Issue\UnimplementedInterfaceMethod;
@ -102,42 +103,42 @@ abstract class ClassLikeChecker implements StatementsSource
/**
* A lookup table for public class properties
*
* @var array<string,array<string,Type\Union>>
* @var array<string,array<string,Type\Union|false>>
*/
protected static $public_class_properties = [];
/**
* A lookup table for protected class properties
*
* @var array<string,array<string,Type\Union>>
* @var array<string,array<string,Type\Union|false>>
*/
protected static $protected_class_properties = [];
/**
* A lookup table for protected class properties
*
* @var array<string,array<string,Type\Union>>
* @var array<string,array<string,Type\Union|false>>
*/
protected static $private_class_properties = [];
/**
* A lookup table for public static class properties
*
* @var array<string,array<string,Type\Union>>
* @var array<string,array<string,Type\Union|false>>
*/
protected static $public_static_class_properties = [];
/**
* A lookup table for protected static class properties
*
* @var array<string,array<string,Type\Union>>
* @var array<string,array<string,Type\Union|false>>
*/
protected static $protected_static_class_properties = [];
/**
* A lookup table for private static class properties
*
* @var array<string,array<string,Type\Union>>
* @var array<string,array<string,Type\Union|false>>
*/
protected static $private_static_class_properties = [];
@ -379,23 +380,35 @@ abstract class ClassLikeChecker implements StatementsSource
$comment = $stmt->getDocComment();
$type_in_comment = null;
if ($comment && $config->use_docblock_types && count($stmt->props) === 1) {
if ($comment && $config->use_docblock_types) {
$type_in_comment = CommentChecker::getTypeFromComment((string) $comment, null, $this);
}
elseif (!$comment && $check_methods) {
if (IssueBuffer::accepts(
new MissingPropertyType(
'Property ' . $this->absolute_class . '::$' . $stmt->props[0]->name . ' does not have a declared type',
$this->file_name,
$stmt->getLine()
),
$this->suppressed_issues
)) {
// fall through
}
}
$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();
$property_type = false;
}
else {
$property_type = StatementsChecker::getSimpleType($property->default) ?: Type::getMixed();
}
}
else {
$property_type = $property_group_type;
$property_type = count($stmt->props) === 1 ? $property_group_type : clone $property_group_type;
}
if ($stmt->isStatic()) {
@ -456,7 +469,7 @@ abstract class ClassLikeChecker implements StatementsSource
);
foreach ($all_instance_properties as $property_name => $property_type) {
$class_context->vars_in_scope['$this->' . $property_name] = $property_type;
$class_context->vars_in_scope['$this->' . $property_name] = $property_type ?: Type::getMixed();
}
$all_static_properties = array_merge(
@ -466,7 +479,7 @@ abstract class ClassLikeChecker implements StatementsSource
);
foreach ($all_static_properties as $property_name => $property_type) {
$class_context->vars_in_scope[$this->absolute_class . '::$' . $property_name] = $property_type;
$class_context->vars_in_scope[$this->absolute_class . '::$' . $property_name] = $property_type ?: Type::getMixed();
}
$config = Config::getInstance();
@ -948,7 +961,7 @@ abstract class ClassLikeChecker implements StatementsSource
/**
* @param string $class_name
* @param mixed $visibility
* @return array<string,Type\Union>
* @return array<string,Type\Union|false>
*/
public static function getInstancePropertiesForClass($class_name, $visibility)
{

View File

@ -27,13 +27,14 @@ use Psalm\Issue\InvalidScope;
use Psalm\Issue\InvalidStaticVariable;
use Psalm\Issue\InvalidPropertyFetch;
use Psalm\Issue\InvisibleProperty;
use Psalm\Issue\MissingPropertyDeclaration;
use Psalm\Issue\MissingPropertyType;
use Psalm\Issue\MixedArgument;
use Psalm\Issue\MixedArrayOffset;
use Psalm\Issue\MixedMethodCall;
use Psalm\Issue\MixedPropertyAssignment;
use Psalm\Issue\MixedPropertyFetch;
use Psalm\Issue\MixedStringOffsetAssignment;
use Psalm\Issue\MissingPropertyDeclaration;
use Psalm\Issue\NoInterfaceProperties;
use Psalm\Issue\NullPropertyAssignment;
use Psalm\Issue\NullPropertyFetch;
@ -800,11 +801,29 @@ class ExpressionChecker
return;
}
if (isset($stmt->inferredType)) {
$stmt->inferredType = Type::combineUnionTypes(clone $class_properties[$stmt->name], $stmt->inferredType);
if ($class_properties[$stmt->name] === false) {
if (IssueBuffer::accepts(
new MissingPropertyType(
'Property ' . $lhs_type_part->value . '::$' . $stmt->name . ' does not have a declared type',
$statements_checker->getCheckedFileName(),
$stmt->getLine()
),
$statements_checker->getSuppressedIssues()
)) {
// fall through
}
$class_property_type = Type::getMixed();
}
else {
$stmt->inferredType = $class_properties[$stmt->name];
$class_property_type = clone $class_properties[$stmt->name];
}
if (isset($stmt->inferredType)) {
$stmt->inferredType = Type::combineUnionTypes($class_property_type, $stmt->inferredType);
}
else {
$stmt->inferredType = $class_property_type;
}
}
@ -836,7 +855,7 @@ class ExpressionChecker
$class_properties = ClassLikeChecker::getInstancePropertiesForClass($context->self, \ReflectionProperty::IS_PRIVATE);
$class_property_types[] = clone $class_properties[$prop_name];
$class_property_types[] = $class_properties[$prop_name] ? clone $class_properties[$prop_name] : Type::getMixed();
$var_id = '$this->' . $prop_name;
}
@ -1027,7 +1046,23 @@ class ExpressionChecker
continue;
}
$class_property_types[] = clone $class_properties[$prop_name];
if ($class_properties[$prop_name] === false) {
if (IssueBuffer::accepts(
new MissingPropertyType(
'Property ' . $lhs_type_part->value . '::$' . $stmt->name . ' does not have a declared type',
$statements_checker->getCheckedFileName(),
$stmt->getLine()
),
$statements_checker->getSuppressedIssues()
)) {
// fall through
}
$class_property_types[] = Type::getMixed();
}
else {
$class_property_types[] = clone $class_properties[$prop_name];
}
}
if (!$has_regular_setter) {
@ -1178,14 +1213,28 @@ class ExpressionChecker
}
}
return;
}
$context->vars_in_scope[$var_id] = $assignment_type;
$class_property_type = clone $class_properties[$prop_name];
if ($class_properties[$prop_name] === false) {
if (IssueBuffer::accepts(
new MissingPropertyType(
'Property ' . $absolute_class . '::$' . $prop_name . ' does not have a declared type',
$statements_checker->getCheckedFileName(),
$stmt->getLine()
),
$statements_checker->getSuppressedIssues()
)) {
// fall through
}
$class_property_type = Type::getMixed();
}
else {
$class_property_type = clone $class_properties[$prop_name];
}
if ($assignment_type->isMixed()) {
return;

View File

@ -1060,7 +1060,7 @@ class TypeChecker
return null;
}
$class_property_type = clone $class_properties[$key_parts[$i]];
$class_property_type = $class_properties[$key_parts[$i]] ? clone $class_properties[$key_parts[$i]] : Type::getMixed();
}
if (!$new_base_type) {

View File

@ -0,0 +1,7 @@
<?php
namespace Psalm\Issue;
class MissingPropertyType extends CodeError
{
}