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

Fix #2438 - improve handling of get_class calls

This commit is contained in:
Matthew Brown 2019-12-07 14:03:20 -05:00
parent d7fdd9b179
commit 3c52102388
4 changed files with 112 additions and 26 deletions

View File

@ -619,36 +619,49 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
}
}
if ($stmt->name instanceof PhpParser\Node\Name &&
($stmt->name->parts === ['get_class'] || $stmt->name->parts === ['gettype']) &&
$stmt->args
if ($stmt->name instanceof PhpParser\Node\Name
&& ($stmt->name->parts === ['get_class'] || $stmt->name->parts === ['gettype'])
) {
$var = $stmt->args[0]->value;
if ($stmt->args) {
$var = $stmt->args[0]->value;
if ($var instanceof PhpParser\Node\Expr\Variable
&& is_string($var->name)
) {
$var_id = '$' . $var->name;
if ($var instanceof PhpParser\Node\Expr\Variable
&& is_string($var->name)
) {
$var_id = '$' . $var->name;
if (isset($context->vars_in_scope[$var_id])) {
$atomic_type = $stmt->name->parts === ['get_class']
? new Type\Atomic\GetClassT($var_id, $context->vars_in_scope[$var_id])
: new Type\Atomic\GetTypeT($var_id);
if (isset($context->vars_in_scope[$var_id])) {
$atomic_type = $stmt->name->parts === ['get_class']
? new Type\Atomic\GetClassT($var_id, $context->vars_in_scope[$var_id])
: new Type\Atomic\GetTypeT($var_id);
$statements_analyzer->node_data->setType($stmt, new Type\Union([$atomic_type]));
}
} elseif ($var_type = $statements_analyzer->node_data->getType($var)) {
$class_string_types = [];
$statements_analyzer->node_data->setType($stmt, new Type\Union([$atomic_type]));
}
} elseif ($var_type = $statements_analyzer->node_data->getType($var)) {
$class_string_types = [];
foreach ($var_type->getTypes() as $class_type) {
if ($class_type instanceof Type\Atomic\TNamedObject) {
$class_string_types[] = new Type\Atomic\TClassString($class_type->value, clone $class_type);
foreach ($var_type->getTypes() as $class_type) {
if ($class_type instanceof Type\Atomic\TNamedObject) {
$class_string_types[] = new Type\Atomic\TClassString($class_type->value, clone $class_type);
}
}
if ($class_string_types) {
$statements_analyzer->node_data->setType($stmt, new Type\Union($class_string_types));
}
}
if ($class_string_types) {
$statements_analyzer->node_data->setType($stmt, new Type\Union($class_string_types));
}
} elseif ($stmt->name->parts === ['get_class']
&& ($get_class_name = $statements_analyzer->getFQCLN())
) {
$statements_analyzer->node_data->setType(
$stmt,
new Type\Union([
new Type\Atomic\TClassString(
$get_class_name,
new Type\Atomic\TNamedObject($get_class_name)
)
])
);
}
}

View File

@ -155,11 +155,27 @@ class NewAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\CallAna
if ($lhs_type_part instanceof Type\Atomic\TLiteralClassString
|| $lhs_type_part instanceof Type\Atomic\TClassString
|| $lhs_type_part instanceof Type\Atomic\GetClassT
) {
if (!$statements_analyzer->node_data->getType($stmt)) {
$class_name = $lhs_type_part instanceof Type\Atomic\TClassString
? $lhs_type_part->as
: $lhs_type_part->value;
if ($lhs_type_part instanceof Type\Atomic\TClassString) {
$class_name = $lhs_type_part->as;
} elseif ($lhs_type_part instanceof Type\Atomic\GetClassT) {
$class_name = 'object';
if ($lhs_type_part->as_type
&& $lhs_type_part->as_type->hasObjectType()
&& $lhs_type_part->as_type->isSingle()
) {
foreach ($lhs_type_part->as_type->getTypes() as $typeof_type_atomic) {
if ($typeof_type_atomic instanceof Type\Atomic\TNamedObject) {
$class_name = $typeof_type_atomic->value;
}
}
}
} else {
$class_name = $lhs_type_part->value;
}
if ($lhs_type_part instanceof Type\Atomic\TClassString) {
$can_extend = true;

View File

@ -232,6 +232,21 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
}
$intersection_types = $lhs_type_part->as_type->extra_types;
} elseif ($lhs_type_part instanceof Type\Atomic\GetClassT
&& !$lhs_type_part->as_type->hasObject()
) {
$fq_class_name = 'object';
if ($lhs_type_part->as_type
&& $lhs_type_part->as_type->hasObjectType()
&& $lhs_type_part->as_type->isSingle()
) {
foreach ($lhs_type_part->as_type->getTypes() as $typeof_type_atomic) {
if ($typeof_type_atomic instanceof Type\Atomic\TNamedObject) {
$fq_class_name = $typeof_type_atomic->value;
}
}
}
} elseif ($lhs_type_part instanceof Type\Atomic\TLiteralClassString) {
$fq_class_name = $lhs_type_part->value;

View File

@ -618,6 +618,48 @@ class ClassStringTest extends TestCase
new \RuntimeException();
}',
],
'createNewObjectFromGetClass' => [
'<?php
class Example {
static function staticMethod(): string {
return "";
}
public function instanceMethod(): string {
$className = get_class();
return $className::staticMethod();
}
}
/**
* @param class-string<Example> $className
*/
function example(string $className, Example $object): string {
$objectClassName = get_class($object);
takesExampleClassString($className);
takesExampleClassString($objectClassName);
if (rand(0, 1)) {
return (new $className)->instanceMethod();
}
if (rand(0, 1)) {
return (new $objectClassName)->instanceMethod();
}
if (rand(0, 1)) {
return $className::staticMethod();
}
return $objectClassName::staticMethod();
}
/** @param class-string<Example> $className */
function takesExampleClassString(string $className): void {}'
],
];
}