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

Build up file-class references on initial scan

This commit is contained in:
Matt Brown 2017-07-27 16:12:16 -04:00
parent 2c1a0b197e
commit aca295ef7d
5 changed files with 62 additions and 31 deletions

View File

@ -962,11 +962,6 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
}
}
FileReferenceProvider::addFileReferenceToClass(
$code_location->file_path,
strtolower($fq_class_name)
);
return true;
}

View File

@ -762,11 +762,12 @@ class ProjectChecker
/**
* @param string $fq_classlike_name
* @param string|null $referencing_file_path
* @param bool $analyze_too
*
* @return void
*/
public function queueClassLikeForScanning($fq_classlike_name, $analyze_too = false)
public function queueClassLikeForScanning($fq_classlike_name, $referencing_file_path = null, $analyze_too = false)
{
if (!$this->config) {
throw new \UnexpectedValueException('Config should not be null here');
@ -781,6 +782,11 @@ class ProjectChecker
$this->classes_to_deep_scan[$fq_classlike_name_lc] = true;
}
}
if ($referencing_file_path) {
FileReferenceProvider::addFileReferenceToClass($referencing_file_path, $fq_classlike_name_lc);
}
}
/**

View File

@ -244,21 +244,29 @@ abstract class Atomic
/**
* @param ProjectChecker $project_checker
* @param string $referencing_file_path
* @param array<string, mixed> $phantom_classes
*
* @return void
*/
public function queueClassLikesForScanning(ProjectChecker $project_checker, array $phantom_classes = [])
{
public function queueClassLikesForScanning(
ProjectChecker $project_checker,
$referencing_file_path = null,
array $phantom_classes = []
) {
if ($this instanceof TNamedObject && !isset($phantom_classes[strtolower($this->value)])) {
$project_checker->queueClassLikeForScanning($this->value);
$project_checker->queueClassLikeForScanning($this->value, $referencing_file_path);
return;
}
if ($this instanceof Type\Atomic\TArray || $this instanceof Type\Atomic\TGenericObject) {
foreach ($this->type_params as $type_param) {
$type_param->queueClassLikesForScanning($project_checker, $phantom_classes);
$type_param->queueClassLikesForScanning(
$project_checker,
$referencing_file_path,
$phantom_classes
);
}
}
}

View File

@ -383,14 +383,22 @@ class Union
/**
* @param ProjectChecker $project_checker
* @param string $referencing_file_path
* @param array<string, mixed> $phantom_classes
*
* @return void
*/
public function queueClassLikesForScanning(ProjectChecker $project_checker, array $phantom_classes = [])
{
public function queueClassLikesForScanning(
ProjectChecker $project_checker,
$referencing_file_path = null,
array $phantom_classes = []
) {
foreach ($this->types as $atomic_type) {
$atomic_type->queueClassLikesForScanning($project_checker, $phantom_classes);
$atomic_type->queueClassLikesForScanning(
$project_checker,
$referencing_file_path,
$phantom_classes
);
}
}
}

View File

@ -217,13 +217,17 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
if ($node->extends) {
$parent_fqcln = ClassLikeChecker::getFQCLNFromNameObject($node->extends, $this->aliases);
$this->project_checker->queueClassLikeForScanning($parent_fqcln, $this->scan_deep);
$this->project_checker->queueClassLikeForScanning(
$parent_fqcln,
$this->file_path,
$this->scan_deep
);
$storage->parent_classes[] = strtolower($parent_fqcln);
}
foreach ($node->implements as $interface) {
$interface_fqcln = ClassLikeChecker::getFQCLNFromNameObject($interface, $this->aliases);
$this->project_checker->queueClassLikeForScanning($interface_fqcln);
$this->project_checker->queueClassLikeForScanning($interface_fqcln, $this->file_path);
$storage->class_implements[strtolower($interface_fqcln)] = $interface_fqcln;
}
} elseif ($node instanceof PhpParser\Node\Stmt\Interface_) {
@ -231,7 +235,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
foreach ($node->extends as $interface) {
$interface_fqcln = ClassLikeChecker::getFQCLNFromNameObject($interface, $this->aliases);
$this->project_checker->queueClassLikeForScanning($interface_fqcln);
$this->project_checker->queueClassLikeForScanning($interface_fqcln, $this->file_path);
$storage->parent_interfaces[strtolower($interface_fqcln)] = $interface_fqcln;
}
} elseif ($node instanceof PhpParser\Node\Stmt\Trait_) {
@ -253,7 +257,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
$fq_classlike_name = ClassLikeChecker::getFQCLNFromNameObject($node->class, $this->aliases);
if (!in_array($fq_classlike_name, ['self', 'static', 'parent'], true)) {
$this->project_checker->queueClassLikeForScanning($fq_classlike_name);
$this->project_checker->queueClassLikeForScanning($fq_classlike_name, $this->file_path);
}
} elseif ($node instanceof PhpParser\Node\Stmt\TryCatch) {
foreach ($node->catches as $catch) {
@ -261,7 +265,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
$catch_fqcln = ClassLikeChecker::getFQCLNFromNameObject($catch_type, $this->aliases);
if (!in_array($catch_fqcln, ['self', 'static', 'parent'], true)) {
$this->project_checker->queueClassLikeForScanning($catch_fqcln);
$this->project_checker->queueClassLikeForScanning($catch_fqcln, $this->file_path);
}
}
}
@ -279,14 +283,17 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
if ($function_params) {
foreach ($function_params as $function_param_group) {
foreach ($function_param_group as $function_param) {
$function_param->type->queueClassLikesForScanning($this->project_checker);
$function_param->type->queueClassLikesForScanning(
$this->project_checker,
$this->file_path
);
}
}
}
$return_type = FunctionChecker::getReturnTypeFromCallMap($function_id);
$return_type->queueClassLikesForScanning($this->project_checker);
$return_type->queueClassLikesForScanning($this->project_checker, $this->file_path);
if ($function_id === 'get_class') {
$this->queue_strings_as_possible_type = true;
@ -322,7 +329,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
foreach ($node->traits as $trait) {
$trait_fqcln = ClassLikeChecker::getFQCLNFromNameObject($trait, $this->aliases);
$this->project_checker->queueClassLikeForScanning($trait_fqcln, $this->scan_deep);
$this->project_checker->queueClassLikeForScanning($trait_fqcln, $this->file_path, $this->scan_deep);
$storage->used_traits[strtolower($trait_fqcln)] = $trait_fqcln;
}
} elseif ($node instanceof PhpParser\Node\Stmt\Property) {
@ -333,7 +340,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
$this->visitInclude($node);
} elseif ($node instanceof PhpParser\Node\Scalar\String_ && $this->queue_strings_as_possible_type) {
if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $node->value)) {
$this->project_checker->queueClassLikeForScanning($node->value);
$this->project_checker->queueClassLikeForScanning($node->value, $this->file_path);
}
} elseif ($node instanceof PhpParser\Node\Expr\Assign
|| $node instanceof PhpParser\Node\Expr\AssignOp
@ -352,7 +359,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
if ($var_comment) {
$var_type = Type::parseString($var_comment->type);
$var_type->queueClassLikesForScanning($this->project_checker);
$var_type->queueClassLikesForScanning($this->project_checker, $this->file_path);
}
}
} elseif ($node instanceof PhpParser\Node\Stmt\Const_) {
@ -384,7 +391,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
foreach ($public_mapped_properties as $property_name => $public_mapped_property) {
$property_type = Type::parseString($public_mapped_property);
$property_type->queueClassLikesForScanning($this->project_checker);
$property_type->queueClassLikesForScanning($this->project_checker, $this->file_path);
if (!isset($storage->properties[$property_name])) {
$storage->properties[$property_name] = new PropertyStorage();
@ -573,7 +580,10 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
);
if (!in_array($return_type_fq_classlike_name, ['self', 'static', 'parent'], true)) {
$this->project_checker->queueClassLikeForScanning($return_type_fq_classlike_name);
$this->project_checker->queueClassLikeForScanning(
$return_type_fq_classlike_name,
$this->file_path
);
}
$return_type_string = $return_type_fq_classlike_name . $suffix;
@ -588,7 +598,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
FunctionLikeChecker::RETURN_TYPE_REGEX
);
$storage->return_type->queueClassLikesForScanning($this->project_checker);
$storage->return_type->queueClassLikesForScanning($this->project_checker, $this->file_path);
$storage->signature_return_type = $storage->return_type;
$storage->signature_return_type_location = $storage->return_type_location;
@ -698,7 +708,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
try {
$storage->return_type = Type::parseString($fixed_type_string);
$storage->return_type->setFromDocblock();
$storage->return_type->queueClassLikesForScanning($this->project_checker);
$storage->return_type->queueClassLikesForScanning($this->project_checker, $this->file_path);
} catch (\Psalm\Exception\TypeParseTreeException $e) {
if (IssueBuffer::accepts(
new InvalidDocblock(
@ -758,13 +768,13 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
$param_type_string = $param_typehint;
} elseif ($param_typehint instanceof PhpParser\Node\Name\FullyQualified) {
$param_type_string = (string)$param_typehint;
$this->project_checker->queueClassLikeForScanning($param_type_string);
$this->project_checker->queueClassLikeForScanning($param_type_string, $this->file_path);
} elseif ($param_typehint->parts === ['self']) {
$param_type_string = $this->fq_classlike_name;
} else {
$param_type_string = ClassLikeChecker::getFQCLNFromNameObject($param_typehint, $this->aliases);
if (!in_array($param_type_string, ['self', 'static', 'parent'], true)) {
$this->project_checker->queueClassLikeForScanning($param_type_string);
$this->project_checker->queueClassLikeForScanning($param_type_string, $this->file_path);
}
}
@ -872,7 +882,11 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
)
);
$new_param_type->queueClassLikesForScanning($this->project_checker, $storage->template_types ?: []);
$new_param_type->queueClassLikesForScanning(
$this->project_checker,
$this->file_path,
$storage->template_types ?: []
);
if ($docblock_param_variadic) {
$new_param_type = new Type\Union([
@ -968,7 +982,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
$property_group_type = $var_comment ? Type::parseString($var_comment->type) : null;
if ($property_group_type) {
$property_group_type->queueClassLikesForScanning($this->project_checker);
$property_group_type->queueClassLikesForScanning($this->project_checker, $this->file_path);
$property_group_type->setFromDocblock();
}