1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Add more issues to solve

This commit is contained in:
Matthew Brown 2016-08-07 11:35:27 -04:00
parent d254bc0aa2
commit 4d26b9b3af
4 changed files with 103 additions and 22 deletions

View File

@ -43,7 +43,7 @@ class ClassChecker implements StatementsSource
protected static $_existing_classes = [];
protected static $_existing_classes_ci = [];
protected static $_existing_interfaces = [];
protected static $_existing_interfaces_ci = [];
protected static $_class_implements = [];
protected static $_class_methods = [];
@ -292,11 +292,7 @@ class ClassChecker implements StatementsSource
public static function classExists($absolute_class)
{
if (isset(self::$_existing_classes_ci[$absolute_class])) {
return true;
}
if (isset(self::$_existing_classes[$absolute_class])) {
if (isset(self::$_existing_classes_ci[strtolower($absolute_class)])) {
return true;
}
@ -305,7 +301,7 @@ class ClassChecker implements StatementsSource
}
if (class_exists($absolute_class, true)) {
self::$_existing_classes_ci[$absolute_class] = true;
self::$_existing_classes_ci[strtolower($absolute_class)] = true;
return true;
}
@ -314,12 +310,12 @@ class ClassChecker implements StatementsSource
public static function interfaceExists($absolute_class)
{
if (isset(self::$_existing_interfaces[$absolute_class])) {
if (isset(self::$_existing_interfaces_ci[strtolower($absolute_class)])) {
return true;
}
if (interface_exists($absolute_class, true)) {
self::$_existing_interfaces[$absolute_class] = true;
self::$_existing_interfaces_ci[strtolower($absolute_class)] = true;
return true;
}
@ -395,7 +391,7 @@ class ClassChecker implements StatementsSource
return;
}
if (!isset(self::$_existing_classes[$absolute_class]) && strpos($absolute_class, '\\') === false) {
if (isset(self::$_existing_classes_ci[strtolower($absolute_class)])) {
$reflection_class = new ReflectionClass($absolute_class);
if ($reflection_class->getName() !== $absolute_class) {
@ -408,7 +404,6 @@ class ClassChecker implements StatementsSource
}
}
self::$_existing_classes[$absolute_class] = true;
return true;
}
@ -495,7 +490,12 @@ class ClassChecker implements StatementsSource
protected static function _registerClassProperties($class_name)
{
$reflected_class = new ReflectionClass($class_name);
try {
$reflected_class = new ReflectionClass($class_name);
}
catch (\ReflectionException $e) {
return false;
}
if ($reflected_class->isUserDefined()) {
$class_file_name = $reflected_class->getFileName();
@ -544,7 +544,9 @@ class ClassChecker implements StatementsSource
public static function getInstancePropertiesForClass($class_name, $visibility)
{
if (!isset(self::$_public_class_properties[$class_name])) {
self::_registerClassProperties($class_name);
if (self::_registerClassProperties($class_name) === false) {
return [];
}
}
if ($visibility === ReflectionProperty::IS_PUBLIC) {

View File

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

View File

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

View File

@ -10,6 +10,8 @@ use Psalm\Issue\InvalidArgument;
use Psalm\Issue\InvalidNamespace;
use Psalm\Issue\InvalidIterator;
use Psalm\Issue\MixedMethodCall;
use Psalm\Issue\MissingPropertyDeclaration;
use Psalm\Issue\NoInterfaceProperties;
use Psalm\Issue\NullPropertyFetch;
use Psalm\Issue\NullReference;
use Psalm\Issue\ParentNotFound;
@ -21,6 +23,7 @@ use Psalm\Issue\InvalidScalarArgument;
use Psalm\Issue\InvalidScope;
use Psalm\Issue\InvalidStaticVariable;
use Psalm\Issue\FailedTypeResolution;
use Psalm\Issue\UndefinedClass;
use Psalm\Issue\UndefinedConstant;
use Psalm\Issue\UndefinedFunction;
use Psalm\Issue\UndefinedProperty;
@ -1099,15 +1102,45 @@ class StatementsChecker
continue;
}
if (!ClassChecker::classExists($lhs_type_part->value)) {
if (ClassChecker::interfaceExists($lhs_type_part->value)) {
if (IssueBuffer::accepts(
new NoInterfaceProperties(
'Interfaces cannot have properties',
$this->_file_name,
$stmt->getLine()
),
$this->_suppressed_issues
)) {
return false;
}
return;
}
if (IssueBuffer::accepts(
new UndefinedClass(
'Cannot get properties of undefined class ' . $lhs_type_part->value,
$this->_file_name,
$stmt->getLine()
),
$this->_suppressed_issues
)) {
return false;
}
return;
}
$class_visibility =
$var_name === 'this'
|| (string) $lhs_type_part === $this->_absolute_class
|| ClassChecker::classExtends((string) $lhs_type_part, $this->_absolute_class)
|| $lhs_type_part->value === $this->_absolute_class
|| ClassChecker::classExtends($lhs_type_part->value, $this->_absolute_class)
? \ReflectionProperty::IS_PRIVATE
: \ReflectionProperty::IS_PUBLIC;
$class_properties = ClassChecker::getInstancePropertiesForClass(
(string) $lhs_type_part,
$lhs_type_part->value,
$class_visibility
);
@ -1173,10 +1206,6 @@ class StatementsChecker
*/
protected function _checkPropertyAssignment($stmt, $prop_name, Type\Union $assignment_type, Context $context)
{
if ($assignment_type->isMixed()) {
return;
}
$class_property_types = [];
if ($stmt instanceof PhpParser\Node\Stmt\PropertyProperty) {
@ -1195,6 +1224,7 @@ class StatementsChecker
$lhs_type = $context->vars_in_scope[$stmt->var->name];
if (!$lhs_type) {
var_dump('no class property types');
// @todo This shouldn't happen
return;
}
@ -1215,11 +1245,19 @@ class StatementsChecker
// @todo NullablePropertyAssignment
}
$has_regular_setter = false;
foreach ($lhs_type->types as $lhs_type_part) {
if ($lhs_type_part->isNull()) {
continue;
}
if (method_exists((string) $lhs_type_part, '__set')) {
continue;
}
$has_regular_setter = true;
if (!$lhs_type_part->isObjectType()) {
if (IssueBuffer::accepts(
new InvalidPropertyAssignment(
@ -1235,7 +1273,12 @@ class StatementsChecker
continue;
}
if ($lhs_type_part->isObject() || (string) $lhs_type_part === 'stdClass') {
if ($lhs_type_part->isObject()) {
continue;
}
if ((string) $lhs_type_part === 'stdClass') {
$class_property_types[] = new Type\Union([$lhs_type_part]);
continue;
}
@ -1258,10 +1301,32 @@ class StatementsChecker
$class_property_types[] = $class_properties[$prop_name];
}
if (!$has_regular_setter) {
return;
}
}
if (count($class_property_types) === 1 && (string) $class_property_types[0] === 'stdClass') {
return;
}
if (!$class_property_types) {
// @todo something here?
if (IssueBuffer::accepts(
new MissingPropertyDeclaration(
'Missing property declaration for $' . $var_id,
$this->_file_name,
$stmt->getLine()
),
$this->_suppressed_issues
)) {
return false;
}
return;
}
if ($assignment_type->isMixed()) {
return;
}