mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Fix #3273 - add support for func_num_args() in conditional type
This commit is contained in:
parent
0d8b56dc78
commit
07e5250292
@ -897,9 +897,15 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
if ($function_storage && $function_storage->template_types) {
|
||||
foreach ($function_storage->template_types as $template_name => $_) {
|
||||
if (!isset($template_result->upper_bounds[$template_name])) {
|
||||
$template_result->upper_bounds[$template_name] = [
|
||||
'fn-' . $function_id => [Type::getEmpty(), 0]
|
||||
];
|
||||
if ($template_name === 'TFunctionArgCount') {
|
||||
$template_result->upper_bounds[$template_name] = [
|
||||
'fn-' . $function_id => [Type::getInt(false, count($stmt->args)), 0]
|
||||
];
|
||||
} else {
|
||||
$template_result->upper_bounds[$template_name] = [
|
||||
'fn-' . $function_id => [Type::getEmpty(), 0]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,9 +140,15 @@ class MethodCallReturnTypeFetcher
|
||||
[$template_type->defining_class]
|
||||
)
|
||||
) {
|
||||
$template_result->upper_bounds[$template_type->param_name] = [
|
||||
($template_type->defining_class) => [Type::getEmpty(), 0]
|
||||
];
|
||||
if ($template_type->param_name === 'TFunctionArgCount') {
|
||||
$template_result->upper_bounds[$template_type->param_name] = [
|
||||
'fn-' . $method_id => [Type::getInt(false, \count($stmt->args)), 0]
|
||||
];
|
||||
} else {
|
||||
$template_result->upper_bounds[$template_type->param_name] = [
|
||||
($template_type->defining_class) => [Type::getEmpty(), 0]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -916,9 +916,15 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
[$template_type->param_name]
|
||||
[$template_type->defining_class]
|
||||
)) {
|
||||
$template_result->upper_bounds[$template_type->param_name] = [
|
||||
($template_type->defining_class) => [Type::getEmpty(), 0]
|
||||
];
|
||||
if ($template_type->param_name === 'TFunctionArgCount') {
|
||||
$template_result->upper_bounds[$template_type->param_name] = [
|
||||
'fn-' . $method_id => [Type::getInt(false, count($stmt->args)), 0]
|
||||
];
|
||||
} else {
|
||||
$template_result->upper_bounds[$template_type->param_name] = [
|
||||
($template_type->defining_class) => [Type::getEmpty(), 0]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2513,16 +2513,15 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
// This checks for param references in the return type tokens
|
||||
// If found, the param is replaced with a generated template param
|
||||
foreach ($fixed_type_tokens as $i => $type_token) {
|
||||
if ($type_token[0][0] === '$') {
|
||||
$token_body = $type_token[0];
|
||||
$token_body = $type_token[0];
|
||||
$template_function_id = 'fn-' . strtolower($cased_function_id);
|
||||
|
||||
if ($token_body[0] === '$') {
|
||||
foreach ($storage->params as $j => $param_storage) {
|
||||
if ('$' . $param_storage->name === $token_body) {
|
||||
if (!isset($param_type_mapping[$token_body])) {
|
||||
$template_name = 'TGeneratedFromParam' . $j;
|
||||
|
||||
$template_function_id = 'fn-' . strtolower($cased_function_id);
|
||||
|
||||
$template_as_type = $param_storage->type
|
||||
? clone $param_storage->type
|
||||
: Type::getMixed();
|
||||
@ -2556,9 +2555,26 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
}
|
||||
|
||||
$fixed_type_tokens[$i][0] = $param_type_mapping[$token_body];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($token_body === 'func_num_args()') {
|
||||
$template_name = 'TFunctionArgCount';
|
||||
|
||||
$storage->template_types[$template_name] = [
|
||||
$template_function_id => [
|
||||
Type::getInt()
|
||||
],
|
||||
];
|
||||
|
||||
$this->function_template_types[$template_name]
|
||||
= $storage->template_types[$template_name];
|
||||
|
||||
$fixed_type_tokens[$i][0] = $template_name;
|
||||
}
|
||||
}
|
||||
|
||||
$storage->return_type = Type::parseTokens(
|
||||
|
@ -1147,6 +1147,17 @@ abstract class Type
|
||||
|| $char === '&'
|
||||
|| $char === '='
|
||||
) {
|
||||
if ($char === '('
|
||||
&& $type_tokens[$rtc][0] === 'func_num_args'
|
||||
&& isset($chars[$i + 1])
|
||||
&& $chars[$i + 1] === ')'
|
||||
) {
|
||||
$type_tokens[$rtc][0] = 'func_num_args()';
|
||||
++$i;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type_tokens[$rtc][0] === '') {
|
||||
$type_tokens[$rtc] = [$char, $i];
|
||||
} else {
|
||||
|
@ -439,6 +439,28 @@ class ConditionalReturnTypeTest extends TestCase
|
||||
}
|
||||
}'
|
||||
],
|
||||
'conditionalOnArgCount' => [
|
||||
'<?php
|
||||
/**
|
||||
* @return (func_num_args() is 0 ? false : string)
|
||||
*/
|
||||
function zeroArgsFalseOneArgString(string $s = "") {
|
||||
if (func_num_args() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
$a = zeroArgsFalseOneArgString();
|
||||
$b = zeroArgsFalseOneArgString("");
|
||||
$c = zeroArgsFalseOneArgString("hello");',
|
||||
[
|
||||
'$a' => 'false',
|
||||
'$b' => 'string',
|
||||
'$c' => 'string',
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user