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:
parent
ce253ad5c3
commit
f6066aaded
@ -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;
|
||||
}
|
||||
|
@ -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_) {
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
*/
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user