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

Log references to parent classes

This commit is contained in:
Matthew Brown 2017-03-10 19:36:17 -05:00
parent ce253ad5c3
commit f6066aaded
5 changed files with 70 additions and 19 deletions

View File

@ -3,6 +3,7 @@ namespace Psalm\Checker;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser;
use Psalm\CodeLocation;
use Psalm\Context;
use Psalm\StatementsSource;
use Psalm\Type;
@ -14,6 +15,11 @@ trait CanAlias
*/
private $aliased_classes = [];
/**
* @var array<string, CodeLocation>
*/
private $aliased_class_locations = [];
/**
* @var array<string, string>
*/
@ -54,9 +60,12 @@ trait CanAlias
$project_checker->use_referencing_locations[strtolower($use_path)][$this->getFilePath()][] =
new \Psalm\CodeLocation($this, $use);
$project_checker->use_referencing_locations[$this->getFilePath()][strtolower($use_path)] = true;
}
$this->aliased_classes[strtolower($use->alias)] = $use_path;
$this->aliased_class_locations[strtolower($use->alias)] = new CodeLocation($this, $stmt);
$this->aliased_classes_flipped[strtolower($use_path)] = $use->alias;
break;
}

View File

@ -249,7 +249,8 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
if ($this instanceof InterfaceChecker) {
$parent_interfaces = InterfaceChecker::getParentInterfaces(
$this->fq_class_name
$this->fq_class_name,
$this->getFileChecker()
);
$extra_interfaces = $parent_interfaces;
@ -270,7 +271,8 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
$extra_interfaces = array_merge(
$extra_interfaces,
InterfaceChecker::getParentInterfaces(
$interface_name
$interface_name,
$this->getFileChecker()
)
);
@ -416,14 +418,39 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
$storage = self::$storage[strtolower($fq_class_name)];
if (($this->class instanceof PhpParser\Node\Stmt\Class_ ||
$this->class instanceof PhpParser\Node\Stmt\Interface_
) &&
$this->parent_fq_class_name &&
!ClassLikeChecker::classOrInterfaceExists($this->parent_fq_class_name, $this->getFileChecker())
) {
// we should not normally get here
return;
if ($this->class instanceof PhpParser\Node\Stmt\Class_ && $this->class->extends) {
if (!$this->parent_fq_class_name) {
throw new \UnexpectedValueException('Parent class should be filled in');
}
$parent_reference_location = new CodeLocation($this, $this->class->extends);
if (!ClassLikeChecker::classOrInterfaceExists(
$this->parent_fq_class_name,
$this->getFileChecker(),
$parent_reference_location
)) {
// we should not normally get here
return;
}
} elseif ($this->class instanceof PhpParser\Node\Stmt\Interface_ && $this->class->extends) {
foreach ($this->class->extends as $extended_interface) {
$extended_interface_name = self::getFQCLNFromNameObject(
$extended_interface,
$this
);
$parent_reference_location = new CodeLocation($this, $extended_interface);
if (!ClassLikeChecker::classOrInterfaceExists(
$extended_interface_name,
$this->getFileChecker(),
$parent_reference_location
)) {
// we should not normally get here
return;
}
}
}
if ($this instanceof ClassChecker && $this->class instanceof PhpParser\Node\Stmt\Class_) {

View File

@ -33,8 +33,6 @@ class InterfaceChecker extends ClassLikeChecker
$this
);
$source->getFileChecker()->evaluateClassLike($extended_interface_name);
$storage->parent_interfaces[] = $extended_interface_name;
}
}
@ -71,18 +69,20 @@ class InterfaceChecker extends ClassLikeChecker
/**
* @param string $interface_name
* @param string $possible_parent
* @param FileChecker $file_checker
* @return boolean
*/
public static function interfaceExtends($interface_name, $possible_parent)
public static function interfaceExtends($interface_name, $possible_parent, FileChecker $file_checker)
{
return in_array($possible_parent, self::getParentInterfaces($interface_name));
return in_array($possible_parent, self::getParentInterfaces($interface_name, $file_checker));
}
/**
* @param string $fq_interface_name
* @param FileChecker $file_checker
* @return array<string> all interfaces extended by $interface_name
*/
public static function getParentInterfaces($fq_interface_name)
public static function getParentInterfaces($fq_interface_name, FileChecker $file_checker)
{
$fq_interface_name = strtolower($fq_interface_name);
@ -97,8 +97,12 @@ class InterfaceChecker extends ClassLikeChecker
foreach ($storage->parent_interfaces as $extended_interface_name) {
$extended_interfaces[] = $extended_interface_name;
if (!self::interfaceExists($extended_interface_name, $file_checker)) {
continue;
}
$extended_interfaces = array_merge(
self::getParentInterfaces($extended_interface_name),
self::getParentInterfaces($extended_interface_name, $file_checker),
$extended_interfaces
);
}

View File

@ -157,10 +157,19 @@ class ProjectChecker
/**
* A map of fully-qualified use declarations to the files
* that reference them (keyed by filename)
*
* @var array<string, array<string, array<int, \Psalm\CodeLocation>>>
*/
public $use_referencing_locations = [];
/**
* A map of file names to the classes that they contain explicit references to
* used in collaboration with use_referencing_locations
*
* @var array<string, array<string, bool>>
*/
public $use_referencing_files = [];
/**
* @var array<string, string>
*/

View File

@ -873,7 +873,8 @@ class TypeChecker
(InterfaceChecker::interfaceExists($input_type_part->value, $file_checker) &&
InterfaceChecker::interfaceExtends(
$input_type_part->value,
$container_type_part->value
$container_type_part->value,
$file_checker
)
) ||
ExpressionChecker::isMock($input_type_part->value)
@ -1015,7 +1016,8 @@ class TypeChecker
(InterfaceChecker::interfaceExists($container_type_part->value, $file_checker) &&
InterfaceChecker::interfaceExtends(
$container_type_part->value,
$input_type_part->value
$input_type_part->value,
$file_checker
)
)
)
@ -1354,7 +1356,7 @@ class TypeChecker
}
if (InterfaceChecker::interfaceExists($differing_type, $file_checker) &&
InterfaceChecker::interfaceExtends($differing_type, $simple_declared_type)
InterfaceChecker::interfaceExtends($differing_type, $simple_declared_type, $file_checker)
) {
$is_match = true;
break;