1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Add support for checking inherited constants

This commit is contained in:
Matthew Brown 2016-08-13 19:44:24 -04:00
parent fd63f3e056
commit defc4cebc1
5 changed files with 79 additions and 43 deletions

View File

@ -4,6 +4,7 @@ namespace Psalm\Checker;
use PhpParser; use PhpParser;
use Psalm\StatementsSource; use Psalm\StatementsSource;
use Psalm\Context;
class ClassChecker extends ClassLikeChecker class ClassChecker extends ClassLikeChecker
{ {
@ -21,14 +22,62 @@ class ClassChecker extends ClassLikeChecker
self::$class_implements[$absolute_class] = []; self::$class_implements[$absolute_class] = [];
if ($this->class->extends) {
$this->parent_class = self::getAbsoluteClassFromName($this->class->extends, $this->namespace, $this->aliased_classes);
}
foreach ($class->implements as $interface_name) { foreach ($class->implements as $interface_name) {
$absolute_interface_name = self::getAbsoluteClassFromName($interface_name, $this->namespace, $this->aliased_classes); $absolute_interface_name = self::getAbsoluteClassFromName($interface_name, $this->namespace, $this->aliased_classes);
self::$class_implements[$absolute_class][$absolute_interface_name] = true; self::$class_implements[$absolute_class][$absolute_interface_name] = true;
self::registerClass($absolute_interface_name);
} }
} }
public function check($check_methods = true, Context $class_context = null)
{
if ($this->parent_class) {
if (self::checkAbsoluteClassOrInterface(
$this->parent_class,
$this->file_name,
$this->class->getLine(),
$this->getSuppressedIssues()
) === false
) {
return false;
}
if (!isset(self::$public_class_properties[$this->parent_class])
|| !isset(self::$public_class_constants[$this->parent_class])
|| !isset(self::$class_implements[$this->parent_class])
) {
self::registerClass($this->parent_class);
}
$this->registerInheritedMethods($this->parent_class);
self::$class_implements[$this->absolute_class] += self::$class_implements[$this->parent_class];
}
foreach (self::$class_implements[$this->absolute_class] as $interface_name => $_) {
if (self::checkAbsoluteClassOrInterface(
$interface_name,
$this->file_name,
$this->class->getLine(),
$this->getSuppressedIssues()
) === false
) {
return false;
}
self::registerClass($interface_name);
}
parent::check($check_methods, $class_context);
}
public static function classExists($absolute_class) public static function classExists($absolute_class)
{ {
if (isset(self::$existing_classes_ci[strtolower($absolute_class)])) { if (isset(self::$existing_classes_ci[strtolower($absolute_class)])) {
@ -89,7 +138,11 @@ class ClassChecker extends ClassLikeChecker
public static function getInterfacesForClass($absolute_class) public static function getInterfacesForClass($absolute_class)
{ {
return isset(self::$class_implements[$absolute_class]) ? self::$class_implements[$absolute_class] : []; if (!isset(self::$class_implements[$absolute_class])) {
self::$class_implements[$absolute_class] = class_implements($absolute_class);
}
return self::$class_implements[$absolute_class];
} }
/** /**
@ -113,14 +166,8 @@ class ClassChecker extends ClassLikeChecker
return false; return false;
} }
$class_implementations = class_implements($absolute_class); $class_implementations = self::getInterfacesForClass($absolute_class);
if (!isset($class_implementations[$interface])) { return isset($class_implementations[$interface]);
return false;
}
self::$class_implements[$absolute_class] = $class_implementations;
return true;
} }
} }

View File

@ -68,10 +68,6 @@ abstract class ClassLikeChecker implements StatementsSource
$this->suppressed_issues = $source->getSuppressedIssues(); $this->suppressed_issues = $source->getSuppressedIssues();
$this->parent_class = $this->class->extends
? self::getAbsoluteClassFromName($this->class->extends, $this->namespace, $this->aliased_classes)
: null;
if (self::$this_class || $class instanceof PhpParser\Node\Stmt\Trait_) { if (self::$this_class || $class instanceof PhpParser\Node\Stmt\Trait_) {
self::$class_checkers[$absolute_class] = $this; self::$class_checkers[$absolute_class] = $this;
} }
@ -79,24 +75,6 @@ abstract class ClassLikeChecker implements StatementsSource
public function check($check_methods = true, Context $class_context = null) public function check($check_methods = true, Context $class_context = null)
{ {
if ($this->parent_class) {
if (self::checkAbsoluteClassOrInterface(
$this->parent_class,
$this->file_name,
$this->class->getLine(),
$this->getSuppressedIssues()
) === false
) {
return false;
}
if (!isset(self::$public_class_properties[$this->parent_class]) || !isset(self::$public_class_constants[$this->parent_class])) {
self::registerClass($this->parent_class);
}
$this->registerInheritedMethods($this->parent_class);
}
$config = Config::getInstance(); $config = Config::getInstance();
$leftover_stmts = []; $leftover_stmts = [];
@ -115,17 +93,17 @@ abstract class ClassLikeChecker implements StatementsSource
self::$public_class_constants[$this->absolute_class] = []; self::$public_class_constants[$this->absolute_class] = [];
if ($this->parent_class) {
self::$public_class_properties[$this->absolute_class] = self::$public_class_properties[$this->parent_class];
self::$protected_class_properties[$this->absolute_class] = self::$protected_class_properties[$this->parent_class];
self::$public_static_class_properties[$this->absolute_class] = self::$public_static_class_properties[$this->parent_class];
self::$protected_static_class_properties[$this->absolute_class] = self::$protected_static_class_properties[$this->parent_class];
self::$public_class_constants[$this->absolute_class] = self::$public_class_constants[$this->parent_class];
}
if ($this instanceof ClassChecker) { if ($this instanceof ClassChecker) {
if ($this->parent_class) {
self::$public_class_properties[$this->absolute_class] = self::$public_class_properties[$this->parent_class];
self::$protected_class_properties[$this->absolute_class] = self::$protected_class_properties[$this->parent_class];
self::$public_static_class_properties[$this->absolute_class] = self::$public_static_class_properties[$this->parent_class];
self::$protected_static_class_properties[$this->absolute_class] = self::$protected_static_class_properties[$this->parent_class];
self::$public_class_constants[$this->absolute_class] = self::$public_class_constants[$this->parent_class];
}
foreach (ClassChecker::getInterfacesForClass($this->absolute_class) as $interface_name => $_) { foreach (ClassChecker::getInterfacesForClass($this->absolute_class) as $interface_name => $_) {
self::$public_class_constants[$this->absolute_class] += self::$public_class_constants[$interface_name]; self::$public_class_constants[$this->absolute_class] += self::$public_class_constants[$interface_name];
} }
@ -531,7 +509,6 @@ abstract class ClassLikeChecker implements StatementsSource
self::$private_class_properties[$class_name][$class_property->getName()] = Type::getMixed(); self::$private_class_properties[$class_name][$class_property->getName()] = Type::getMixed();
} }
} }
} }
$class_constants = $reflected_class->getConstants(); $class_constants = $reflected_class->getConstants();
@ -570,6 +547,10 @@ abstract class ClassLikeChecker implements StatementsSource
self::$public_class_constants[$class_name][$name] = $const_type; self::$public_class_constants[$class_name][$name] = $const_type;
} }
if (!$reflected_class->isTrait() && !$reflected_class->isInterface()) {
ClassChecker::getInterfacesForClass($class_name);
}
} }
} }

View File

@ -9,6 +9,7 @@ use Psalm\Issue\InvalidDocblock;
use Psalm\Issue\InvalidReturnType; use Psalm\Issue\InvalidReturnType;
use Psalm\StatementsSource; use Psalm\StatementsSource;
use Psalm\Type; use Psalm\Type;
use Psalm\Config;
use Psalm\Context; use Psalm\Context;
use Psalm\IssueBuffer; use Psalm\IssueBuffer;

View File

@ -8,6 +8,8 @@ use Psalm\Context;
class InterfaceChecker extends ClassLikeChecker class InterfaceChecker extends ClassLikeChecker
{ {
protected $parent_interfaces = [];
protected static $existing_interfaces = []; protected static $existing_interfaces = [];
protected static $existing_interfaces_ci = []; protected static $existing_interfaces_ci = [];
@ -17,6 +19,10 @@ class InterfaceChecker extends ClassLikeChecker
self::$existing_interfaces[$absolute_class] = true; self::$existing_interfaces[$absolute_class] = true;
self::$existing_interfaces_ci[strtolower($absolute_class)] = true; self::$existing_interfaces_ci[strtolower($absolute_class)] = true;
foreach ($interface->extends as $extended_interface) {
$this->parent_interfaces[] = self::getAbsoluteClassFromName($extended_interface, $this->namespace, $this->aliased_classes);
}
} }
public static function interfaceExists($absolute_class) public static function interfaceExists($absolute_class)

View File

@ -7,6 +7,7 @@ use RecursiveIteratorIterator;
use Psalm\Config; use Psalm\Config;
use Psalm\IssueBuffer; use Psalm\IssueBuffer;
use Psalm\Exception;
class ProjectChecker class ProjectChecker
{ {