mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
parent
74934ffdbb
commit
5bcd1bbb75
@ -140,6 +140,15 @@ class SwitchCaseAnalyzer
|
||||
),
|
||||
]
|
||||
);
|
||||
} elseif ($type instanceof Type\Atomic\TDependentGetDebugType) {
|
||||
$type_statements[] = new PhpParser\Node\Expr\FuncCall(
|
||||
new PhpParser\Node\Name(['get_debug_type']),
|
||||
[
|
||||
new PhpParser\Node\Arg(
|
||||
new PhpParser\Node\Expr\Variable(substr($type->typeof, 1))
|
||||
),
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$type_statements = null;
|
||||
break;
|
||||
|
@ -568,6 +568,7 @@ class AssertionFinder
|
||||
$true_position = self::hasTrueVariable($conditional);
|
||||
$empty_array_position = self::hasEmptyArrayVariable($conditional);
|
||||
$gettype_position = self::hasGetTypeCheck($conditional);
|
||||
$get_debug_type_position = self::hasGetDebugTypeCheck($conditional);
|
||||
$min_count = null;
|
||||
$count_equality_position = self::hasNonEmptyCountEqualityCheck($conditional, $min_count);
|
||||
|
||||
@ -965,6 +966,52 @@ class AssertionFinder
|
||||
return $if_types;
|
||||
}
|
||||
|
||||
if ($get_debug_type_position) {
|
||||
if ($get_debug_type_position === self::ASSIGNMENT_TO_RIGHT) {
|
||||
$whichclass_expr = $conditional->left;
|
||||
$get_debug_type_expr = $conditional->right;
|
||||
} elseif ($get_debug_type_position === self::ASSIGNMENT_TO_LEFT) {
|
||||
$whichclass_expr = $conditional->right;
|
||||
$get_debug_type_expr = $conditional->left;
|
||||
} else {
|
||||
throw new \UnexpectedValueException('$gettype_position value');
|
||||
}
|
||||
|
||||
/** @var PhpParser\Node\Expr\FuncCall $get_debug_type_expr */
|
||||
$var_name = ExpressionIdentifier::getArrayVarId(
|
||||
$get_debug_type_expr->args[0]->value,
|
||||
$this_class_name,
|
||||
$source
|
||||
);
|
||||
|
||||
if ($whichclass_expr instanceof PhpParser\Node\Scalar\String_) {
|
||||
$var_type = $whichclass_expr->value;
|
||||
} elseif ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||
&& $whichclass_expr->class instanceof PhpParser\Node\Name
|
||||
) {
|
||||
$var_type = ClassLikeAnalyzer::getFQCLNFromNameObject(
|
||||
$whichclass_expr->class,
|
||||
$source->getAliases()
|
||||
);
|
||||
} else {
|
||||
throw new \UnexpectedValueException('Shouldn’t get here');
|
||||
}
|
||||
|
||||
if ($var_name && $var_type) {
|
||||
if ($var_type === 'class@anonymous') {
|
||||
$if_types[$var_name] = [['=object']];
|
||||
} elseif ($var_type === 'resource (closed)') {
|
||||
$if_types[$var_name] = [['closed-resource']];
|
||||
} elseif (substr($var_type, 0, 10) === 'resource (') {
|
||||
$if_types[$var_name] = [['=resource']];
|
||||
} else {
|
||||
$if_types[$var_name] = [[$var_type]];
|
||||
}
|
||||
}
|
||||
|
||||
return $if_types;
|
||||
}
|
||||
|
||||
if ($count_equality_position) {
|
||||
if ($count_equality_position === self::ASSIGNMENT_TO_RIGHT) {
|
||||
$count_expr = $conditional->left;
|
||||
@ -1206,6 +1253,7 @@ class AssertionFinder
|
||||
$true_position = self::hasTrueVariable($conditional);
|
||||
$empty_array_position = self::hasEmptyArrayVariable($conditional);
|
||||
$gettype_position = self::hasGetTypeCheck($conditional);
|
||||
$get_debug_type_position = self::hasGetDebugTypeCheck($conditional);
|
||||
$count = null;
|
||||
$count_inequality_position = self::hasNotCountEqualityCheck($conditional, $count);
|
||||
|
||||
@ -1631,6 +1679,52 @@ class AssertionFinder
|
||||
return $if_types;
|
||||
}
|
||||
|
||||
if ($get_debug_type_position) {
|
||||
if ($get_debug_type_position === self::ASSIGNMENT_TO_RIGHT) {
|
||||
$whichclass_expr = $conditional->left;
|
||||
$get_debug_type_expr = $conditional->right;
|
||||
} elseif ($get_debug_type_position === self::ASSIGNMENT_TO_LEFT) {
|
||||
$whichclass_expr = $conditional->right;
|
||||
$get_debug_type_expr = $conditional->left;
|
||||
} else {
|
||||
throw new \UnexpectedValueException('$gettype_position value');
|
||||
}
|
||||
|
||||
/** @var PhpParser\Node\Expr\FuncCall $get_debug_type_expr */
|
||||
$var_name = ExpressionIdentifier::getArrayVarId(
|
||||
$get_debug_type_expr->args[0]->value,
|
||||
$this_class_name,
|
||||
$source
|
||||
);
|
||||
|
||||
if ($whichclass_expr instanceof PhpParser\Node\Scalar\String_) {
|
||||
$var_type = $whichclass_expr->value;
|
||||
} elseif ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||
&& $whichclass_expr->class instanceof PhpParser\Node\Name
|
||||
) {
|
||||
$var_type = ClassLikeAnalyzer::getFQCLNFromNameObject(
|
||||
$whichclass_expr->class,
|
||||
$source->getAliases()
|
||||
);
|
||||
} else {
|
||||
throw new \UnexpectedValueException('Shouldn’t get here');
|
||||
}
|
||||
|
||||
if ($var_name && $var_type) {
|
||||
if ($var_type === 'class@anonymous') {
|
||||
$if_types[$var_name] = [['!=object']];
|
||||
} elseif ($var_type === 'resource (closed)') {
|
||||
$if_types[$var_name] = [['!closed-resource']];
|
||||
} elseif (substr($var_type, 0, 10) === 'resource (') {
|
||||
$if_types[$var_name] = [['!=resource']];
|
||||
} else {
|
||||
$if_types[$var_name] = [['!' . $var_type]];
|
||||
}
|
||||
}
|
||||
|
||||
return $if_types;
|
||||
}
|
||||
|
||||
if (!$source instanceof StatementsAnalyzer) {
|
||||
return [];
|
||||
}
|
||||
@ -2622,6 +2716,32 @@ class AssertionFinder
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|int
|
||||
*/
|
||||
protected static function hasGetDebugTypeCheck(PhpParser\Node\Expr\BinaryOp $conditional)
|
||||
{
|
||||
if ($conditional->right instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->right->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($conditional->right->name->parts[0]) === 'get_debug_type'
|
||||
&& $conditional->right->args
|
||||
&& $conditional->left instanceof PhpParser\Node\Scalar\String_
|
||||
) {
|
||||
return self::ASSIGNMENT_TO_RIGHT;
|
||||
}
|
||||
|
||||
if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall
|
||||
&& $conditional->left->name instanceof PhpParser\Node\Name
|
||||
&& strtolower($conditional->left->name->parts[0]) === 'get_debug_type'
|
||||
&& $conditional->left->args
|
||||
&& $conditional->right instanceof PhpParser\Node\Scalar\String_
|
||||
) {
|
||||
return self::ASSIGNMENT_TO_LEFT;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|int
|
||||
*/
|
||||
|
@ -1327,7 +1327,10 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
) : void {
|
||||
$first_arg = isset($stmt->args[0]) ? $stmt->args[0] : null;
|
||||
|
||||
if ($function_name->parts === ['get_class'] || $function_name->parts === ['gettype']) {
|
||||
if ($function_name->parts === ['get_class']
|
||||
|| $function_name->parts === ['gettype']
|
||||
|| $function_name->parts === ['get_debug_type']
|
||||
) {
|
||||
if ($first_arg) {
|
||||
$var = $first_arg->value;
|
||||
|
||||
@ -1337,18 +1340,26 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
$var_id = '$' . $var->name;
|
||||
|
||||
if (isset($context->vars_in_scope[$var_id])) {
|
||||
$atomic_type = $function_name->parts === ['get_class']
|
||||
? new Type\Atomic\TDependentGetClass(
|
||||
if ($function_name->parts === ['get_class']) {
|
||||
$atomic_type = new Type\Atomic\TDependentGetClass(
|
||||
$var_id,
|
||||
$context->vars_in_scope[$var_id]->hasMixed()
|
||||
? Type::getObject()
|
||||
: $context->vars_in_scope[$var_id]
|
||||
)
|
||||
: new Type\Atomic\TDependentGetType($var_id);
|
||||
);
|
||||
} elseif ($function_name->parts === ['get_class']) {
|
||||
$atomic_type = new Type\Atomic\TDependentGetType($var_id);
|
||||
} else {
|
||||
$atomic_type = new Type\Atomic\TDependentGetDebugType($var_id);
|
||||
}
|
||||
|
||||
$statements_analyzer->node_data->setType($real_stmt, new Type\Union([$atomic_type]));
|
||||
}
|
||||
} elseif ($var_type = $statements_analyzer->node_data->getType($var)) {
|
||||
} elseif (($var_type = $statements_analyzer->node_data->getType($var))
|
||||
&& ($function_name->parts === ['get_class']
|
||||
|| $function_name->parts === ['get_debug_type']
|
||||
)
|
||||
) {
|
||||
$class_string_types = [];
|
||||
|
||||
foreach ($var_type->getAtomicTypes() as $class_type) {
|
||||
@ -1374,8 +1385,24 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
$class_type->defining_class
|
||||
);
|
||||
}
|
||||
} else {
|
||||
} elseif ($function_name->parts === ['get_class']) {
|
||||
$class_string_types[] = new Type\Atomic\TClassString();
|
||||
} elseif ($function_name->parts === ['get_debug_type']) {
|
||||
if ($class_type instanceof Type\Atomic\TInt) {
|
||||
$class_string_types[] = new Type\Atomic\TLiteralString('int');
|
||||
} elseif ($class_type instanceof Type\Atomic\TString) {
|
||||
$class_string_types[] = new Type\Atomic\TLiteralString('string');
|
||||
} elseif ($class_type instanceof Type\Atomic\TFloat) {
|
||||
$class_string_types[] = new Type\Atomic\TLiteralString('float');
|
||||
} elseif ($class_type instanceof Type\Atomic\TBool) {
|
||||
$class_string_types[] = new Type\Atomic\TLiteralString('bool');
|
||||
} elseif ($class_type instanceof Type\Atomic\TClosedResource) {
|
||||
$class_string_types[] = new Type\Atomic\TLiteralString('resource (closed)');
|
||||
} elseif ($class_type instanceof Type\Atomic\TNull) {
|
||||
$class_string_types[] = new Type\Atomic\TLiteralString('null');
|
||||
} else {
|
||||
$class_string_types[] = new Type\Atomic\TString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3617,6 +3617,7 @@ return [
|
||||
'get_declared_classes' => ['list<class-string>'],
|
||||
'get_declared_interfaces' => ['list<class-string>'],
|
||||
'get_declared_traits' => ['list<class-string>|null'],
|
||||
'get_debug_type' => ['string', 'data'=>'mixed'],
|
||||
'get_defined_constants' => ['array<string,int|string|float|bool|null|array|resource>', 'categorize='=>'bool'],
|
||||
'get_defined_functions' => ['array<string,array<string,callable-string>>', 'exclude_disabled='=>'bool'],
|
||||
'get_defined_vars' => ['array'],
|
||||
|
28
src/Psalm/Type/Atomic/TDependentGetDebugType.php
Normal file
28
src/Psalm/Type/Atomic/TDependentGetDebugType.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Psalm\Type\Atomic;
|
||||
|
||||
/**
|
||||
* Represents a string whose value is that of a type found by get_debug_type($var)
|
||||
*/
|
||||
class TDependentGetDebugType extends TString
|
||||
{
|
||||
/**
|
||||
* Used to hold information as to what this refers to
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $typeof;
|
||||
|
||||
/**
|
||||
* @param string $typeof the variable id
|
||||
*/
|
||||
public function __construct(string $typeof)
|
||||
{
|
||||
$this->typeof = $typeof;
|
||||
}
|
||||
|
||||
public function canBeFullyExpressedInPhp(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user