mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Make TClassString inherit from TLiteralString
This commit is contained in:
parent
34a39b895c
commit
5bd9ba6207
@ -114,11 +114,20 @@ class NewChecker extends \Psalm\Checker\Statements\Expression\CallChecker
|
||||
}
|
||||
|
||||
if (isset($stmt->class->inferredType)) {
|
||||
$new_type = null;
|
||||
|
||||
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);
|
||||
if ($new_type) {
|
||||
$new_type = Type::combineUnionTypes(
|
||||
$new_type,
|
||||
Type::parseString($lhs_type_part->value)
|
||||
);
|
||||
} else {
|
||||
$new_type = Type::parseString($lhs_type_part->value);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
@ -128,12 +137,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;
|
||||
}
|
||||
|
||||
if (IssueBuffer::accepts(
|
||||
// do nothing
|
||||
} elseif (IssueBuffer::accepts(
|
||||
new InvalidStringClass(
|
||||
'String cannot be used as a class',
|
||||
new CodeLocation($statements_checker->getSource(), $stmt)
|
||||
@ -142,29 +147,15 @@ class NewChecker extends \Psalm\Checker\Statements\Expression\CallChecker
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
|
||||
$stmt->inferredType = Type::getObject();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($lhs_type_part instanceof Type\Atomic\TMixed
|
||||
} elseif ($lhs_type_part instanceof Type\Atomic\TMixed
|
||||
|| $lhs_type_part instanceof Type\Atomic\TGenericParam
|
||||
) {
|
||||
$stmt->inferredType = Type::getObject();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($lhs_type_part instanceof Type\Atomic\TNull
|
||||
// do nothing
|
||||
} elseif ($lhs_type_part instanceof Type\Atomic\TNull
|
||||
&& $stmt->class->inferredType->ignore_nullable_issues
|
||||
) {
|
||||
$stmt->inferredType = Type::getObject();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IssueBuffer::accepts(
|
||||
// do nothing
|
||||
} elseif (IssueBuffer::accepts(
|
||||
new UndefinedClass(
|
||||
'Type ' . $lhs_type_part . ' cannot be called as a class',
|
||||
new CodeLocation($statements_checker->getSource(), $stmt),
|
||||
@ -175,7 +166,18 @@ class NewChecker extends \Psalm\Checker\Statements\Expression\CallChecker
|
||||
// fall through
|
||||
}
|
||||
|
||||
$stmt->inferredType = Type::getObject();
|
||||
if ($new_type) {
|
||||
$new_type = Type::combineUnionTypes(
|
||||
$new_type,
|
||||
Type::getObject()
|
||||
);
|
||||
} else {
|
||||
$new_type = Type::getObject();
|
||||
}
|
||||
}
|
||||
|
||||
if ($new_type) {
|
||||
$stmt->inferredType = $new_type;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1600,19 +1600,22 @@ class CallChecker
|
||||
return false;
|
||||
}
|
||||
} elseif ($param_type_part instanceof TArray
|
||||
&& isset($param_type_part->type_params[1]->getTypes()['class-string'])
|
||||
&& $input_expr instanceof PhpParser\Node\Expr\Array_
|
||||
) {
|
||||
foreach ($input_expr->items as $item) {
|
||||
if ($item && $item->value instanceof PhpParser\Node\Scalar\String_) {
|
||||
if (ClassLikeChecker::checkFullyQualifiedClassLikeName(
|
||||
$statements_checker,
|
||||
$item->value->value,
|
||||
$code_location,
|
||||
$statements_checker->getSuppressedIssues()
|
||||
) === false
|
||||
) {
|
||||
return false;
|
||||
foreach ($param_type_part->type_params[1]->getTypes() as $param_array_type_part) {
|
||||
if ($param_array_type_part instanceof TClassString) {
|
||||
foreach ($input_expr->items as $item) {
|
||||
if ($item && $item->value instanceof PhpParser\Node\Scalar\String_) {
|
||||
if (ClassLikeChecker::checkFullyQualifiedClassLikeName(
|
||||
$statements_checker,
|
||||
$item->value->value,
|
||||
$code_location,
|
||||
$statements_checker->getSuppressedIssues()
|
||||
) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,19 +207,22 @@ class ReturnChecker
|
||||
return false;
|
||||
}
|
||||
} elseif ($local_type_part instanceof Type\Atomic\TArray
|
||||
&& isset($local_type_part->type_params[1]->getTypes()['class-string'])
|
||||
&& $stmt->expr instanceof PhpParser\Node\Expr\Array_
|
||||
) {
|
||||
foreach ($stmt->expr->items as $item) {
|
||||
if ($item && $item->value instanceof PhpParser\Node\Scalar\String_) {
|
||||
if (ClassLikeChecker::checkFullyQualifiedClassLikeName(
|
||||
$statements_checker,
|
||||
$item->value->value,
|
||||
new CodeLocation($source, $item->value),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
) === false
|
||||
) {
|
||||
return false;
|
||||
foreach ($local_type_part->type_params[1]->getTypes() as $local_array_type_part) {
|
||||
if ($local_array_type_part instanceof Type\Atomic\TClassString) {
|
||||
foreach ($stmt->expr->items as $item) {
|
||||
if ($item && $item->value instanceof PhpParser\Node\Scalar\String_) {
|
||||
if (ClassLikeChecker::checkFullyQualifiedClassLikeName(
|
||||
$statements_checker,
|
||||
$item->value->value,
|
||||
new CodeLocation($source, $item->value),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -630,6 +630,23 @@ class TypeChecker
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($container_type_part instanceof TClassString && $input_type_part instanceof TClassString) {
|
||||
if ($container_type_part->value === 'object') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($input_type_part->value === 'object') {
|
||||
$type_coerced = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$fake_container_object = new TNamedObject($container_type_part->value);
|
||||
$fake_input_object = new TNamedObject($container_type_part->value);
|
||||
|
||||
return self::isObjectContainedByObject($codebase, $fake_input_object, $fake_container_object);
|
||||
}
|
||||
|
||||
if ($input_type_part instanceof TClassString
|
||||
&& (get_class($container_type_part) === TString::class
|
||||
|| get_class($container_type_part) === Type\Atomic\GetClassT::class)
|
||||
|
@ -1,19 +1,14 @@
|
||||
<?php
|
||||
namespace Psalm\Type\Atomic;
|
||||
|
||||
class TClassString extends TString
|
||||
class TClassString extends TLiteralString
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @param string $value string
|
||||
*/
|
||||
public $class_type;
|
||||
|
||||
/**
|
||||
* @param string $class_type string
|
||||
*/
|
||||
public function __construct($class_type = 'object')
|
||||
public function __construct($value = 'object')
|
||||
{
|
||||
$this->class_type = $class_type;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
@ -21,19 +16,6 @@ class TClassString extends TString
|
||||
return 'class-string';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getKey()
|
||||
{
|
||||
return 'class-string';
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $namespace
|
||||
* @param array<string> $aliased_classes
|
||||
|
@ -9,7 +9,6 @@ use Psalm\Type\Atomic\ObjectLike;
|
||||
use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TBool;
|
||||
use Psalm\Type\Atomic\TCallable;
|
||||
use Psalm\Type\Atomic\TClassString;
|
||||
use Psalm\Type\Atomic\TEmpty;
|
||||
use Psalm\Type\Atomic\TFalse;
|
||||
use Psalm\Type\Atomic\TFloat;
|
||||
@ -48,9 +47,6 @@ class TypeCombination
|
||||
/** @var bool */
|
||||
public $objectlike_sealed = true;
|
||||
|
||||
/** @var array<string, string> */
|
||||
public $class_string_types = [];
|
||||
|
||||
/** @var array<int, Atomic\TLiteralString>|null */
|
||||
public $strings = [];
|
||||
|
||||
@ -195,10 +191,6 @@ class TypeCombination
|
||||
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) {
|
||||
@ -369,14 +361,6 @@ class TypeCombination
|
||||
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 {
|
||||
if ($type instanceof TString) {
|
||||
if ($type instanceof TLiteralString) {
|
||||
|
@ -500,7 +500,7 @@ class Union
|
||||
*/
|
||||
public function hasString()
|
||||
{
|
||||
return isset($this->types['string']) || isset($this->types['class-string']) || $this->literal_string_types;
|
||||
return isset($this->types['string']) || $this->literal_string_types;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -581,7 +581,7 @@ class AnnotationTest extends TestCase
|
||||
'annotations' => [],
|
||||
'error_levels' => ['TypeCoercion'],
|
||||
],
|
||||
'singleClassConstant' => [
|
||||
'singleClassConstantAsConstant' => [
|
||||
'<?php
|
||||
/**
|
||||
* @param class-string $s
|
||||
|
Loading…
Reference in New Issue
Block a user