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

Allow classes to be instantiated by their name only

Ref #641
This commit is contained in:
Matthew Brown 2018-04-03 22:20:00 -04:00
parent f679900028
commit 4552e69ef2
6 changed files with 56 additions and 5 deletions

View File

@ -117,6 +117,10 @@ class NewChecker extends \Psalm\Checker\Statements\Expression\CallChecker
foreach ($stmt->class->inferredType->getTypes() as $lhs_type_part) {
// this is always OK
if ($lhs_type_part instanceof Type\Atomic\TClassString) {
if (!isset($stmt->inferredType)) {
$stmt->inferredType = Type::parseString($lhs_type_part->class_type);
}
continue;
}
@ -124,6 +128,8 @@ class NewChecker extends \Psalm\Checker\Statements\Expression\CallChecker
if ($config->allow_string_standin_for_class
&& !$lhs_type_part instanceof Type\Atomic\TNumericString
) {
$stmt->inferredType = Type::getObject();
continue;
}
@ -137,16 +143,22 @@ class NewChecker extends \Psalm\Checker\Statements\Expression\CallChecker
// fall through
}
$stmt->inferredType = Type::getObject();
continue;
}
if ($lhs_type_part instanceof Type\Atomic\TMixed) {
$stmt->inferredType = Type::getObject();
continue;
}
if ($lhs_type_part instanceof Type\Atomic\TNull
&& $stmt->class->inferredType->ignore_nullable_issues
) {
$stmt->inferredType = Type::getObject();
continue;
}
@ -160,11 +172,11 @@ class NewChecker extends \Psalm\Checker\Statements\Expression\CallChecker
)) {
// fall through
}
$stmt->inferredType = Type::getObject();
}
}
$stmt->inferredType = Type::getObject();
return null;
}

View File

@ -129,7 +129,7 @@ class ConstFetchChecker
}
if ($stmt->name === 'class') {
$stmt->inferredType = Type::getClassString();
$stmt->inferredType = Type::getClassString($fq_class_name);
return null;
}

View File

@ -555,11 +555,13 @@ abstract class Type
}
/**
* @param string $class_type
*
* @return Type\Union
*/
public static function getClassString()
public static function getClassString($class_type = 'object')
{
$type = new TClassString;
$type = new TClassString($class_type);
return new Union([$type]);
}
@ -856,6 +858,10 @@ abstract class Type
unset($combination->type_params['array']);
}
if ($combination->class_string_types) {
$new_types[] = new TClassString(implode('|', $combination->class_string_types));
}
foreach ($combination->type_params as $generic_type => $generic_type_params) {
if ($generic_type === 'array') {
if ($combination->objectlike_entries) {
@ -993,6 +999,14 @@ abstract class Type
foreach ($possibly_undefined_entries as $type) {
$type->possibly_undefined = true;
}
} elseif ($type instanceof TClassString) {
if (!isset($combination->class_string_types['object'])) {
$class_string_types = explode('|', $type->class_type);
foreach ($class_string_types as $class_string_type) {
$combination->class_string_types[strtolower($class_string_type)] = $class_string_type;
}
}
} else {
$combination->value_types[$type_key] = $type;
}

View File

@ -3,6 +3,18 @@ namespace Psalm\Type\Atomic;
class TClassString extends TString
{
/**
* @var string
*/
public $class_type;
/**
* @param string $class_type string
*/
public function __construct($class_type = 'object') {
$this->class_type = $class_type;
}
public function __toString()
{
return 'class-string';

View File

@ -11,4 +11,7 @@ class TypeCombination
/** @var array<string|int, Union> */
public $objectlike_entries = [];
/** @var array<string, string> */
public $class_string_types = [];
}

View File

@ -753,6 +753,16 @@ class TypeReconciliationTest extends TestCase
function takesNullableString(?string $s) : void {}',
],
'classStringInstantiation' => [
'<?php
class Foo {}
class Bar {}
$class = mt_rand(0, 1) === 1 ? Foo::class : Bar::class;
$object = new $class();',
'assertions' => [
'$object' => 'Foo|Bar',
],
],
];
}