1
0
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:
Brown 2020-05-01 16:02:53 -04:00
parent 0d8b56dc78
commit 07e5250292
6 changed files with 80 additions and 13 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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',
]
],
];
}
}