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:
parent
d7fdd9b179
commit
3c52102388
@ -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)
|
||||
)
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user