1
0
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:
Matthew Brown 2018-05-20 00:27:53 -04:00
parent 34a39b895c
commit 5bd9ba6207
8 changed files with 80 additions and 89 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -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)

View File

@ -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

View File

@ -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) {

View File

@ -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;
}
/**

View File

@ -581,7 +581,7 @@ class AnnotationTest extends TestCase
'annotations' => [],
'error_levels' => ['TypeCoercion'],
],
'singleClassConstant' => [
'singleClassConstantAsConstant' => [
'<?php
/**
* @param class-string $s