1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Allow @psalm-variadic to denote functions that use func_call_args

This commit is contained in:
Matthew Brown 2016-10-18 17:55:07 -04:00
parent 068dc41173
commit a1acaa231a
4 changed files with 65 additions and 3 deletions

View File

@ -56,7 +56,7 @@ class CommentChecker
/** /**
* @param string $comment * @param string $comment
* @psalm-return object-like{return_type:null|string,params:array<object-like{name:string,type:string},deprecated:bool,suppress:array<string>} * @psalm-return object-like{return_type:null|string,params:array<object-like{name:string,type:string},deprecated:bool,suppress:array<string>,variadic:boolean}
*/ */
public static function extractDocblockInfo($comment) public static function extractDocblockInfo($comment)
{ {
@ -108,6 +108,8 @@ class CommentChecker
} }
} }
$info['variadic'] = isset($comments['specials']['psalm-variadic']);
return $info; return $info;
} }
} }

View File

@ -22,6 +22,12 @@ class FunctionChecker extends FunctionLikeChecker
* @var array<string,array<string,array<FunctionLikeParameter>>> * @var array<string,array<string,array<FunctionLikeParameter>>>
*/ */
protected static $file_function_params = []; protected static $file_function_params = [];
/**
* @var array<string,bool>
*/
protected static $variadic_functions = [];
protected static $builtin_function_params = []; protected static $builtin_function_params = [];
protected static $builtin_functions = []; protected static $builtin_functions = [];
protected static $call_map = null; protected static $call_map = null;
@ -74,6 +80,16 @@ class FunctionChecker extends FunctionLikeChecker
return self::$file_function_params[$file_name][$function_id]; return self::$file_function_params[$file_name][$function_id];
} }
/**
* @param string $function_id
* @param string $file_name
* @return boolean
*/
public static function isVariadic($function_id, $file_name)
{
return isset(self::$variadic_functions[$file_name][$function_id]);
}
/** /**
* @param string $function_id * @param string $function_id
* @return void * @return void
@ -153,6 +169,10 @@ class FunctionChecker extends FunctionLikeChecker
self::$deprecated_functions[$file_name][$function_id] = true; self::$deprecated_functions[$file_name][$function_id] = true;
} }
if ($docblock_info['variadic']) {
self::$variadic_functions[$file_name][$function_id] = true;
}
$this->suppressed_issues = $docblock_info['suppress']; $this->suppressed_issues = $docblock_info['suppress'];
if ($config->use_docblock_types) { if ($config->use_docblock_types) {

View File

@ -53,6 +53,12 @@ class MethodChecker extends FunctionLikeChecker
protected static $method_suppress = []; protected static $method_suppress = [];
protected static $deprecated_methods = []; protected static $deprecated_methods = [];
/**
* A dictionary of variadic methods
* @var array<string,bool>
*/
protected static $variadic_methods = [];
const VISIBILITY_PUBLIC = 1; const VISIBILITY_PUBLIC = 1;
const VISIBILITY_PROTECTED = 2; const VISIBILITY_PROTECTED = 2;
const VISIBILITY_PRIVATE = 3; const VISIBILITY_PRIVATE = 3;
@ -82,6 +88,19 @@ class MethodChecker extends FunctionLikeChecker
return self::$method_params[$method_id]; return self::$method_params[$method_id];
} }
/**
* @param string $method_id
* @return boolean
*/
public static function isVariadic($method_id)
{
self::registerClassMethod($method_id);
$method_id = self::getDeclaringMethodId($method_id);
return isset(self::$variadic_methods[$method_id]);
}
/** /**
* @param string $method_id * @param string $method_id
* @return Type\Union|null * @return Type\Union|null
@ -237,6 +256,10 @@ class MethodChecker extends FunctionLikeChecker
self::$deprecated_methods[$method_id] = true; self::$deprecated_methods[$method_id] = true;
} }
if ($docblock_info['variadic']) {
self::$variadic_methods[$method_id] = true;
}
$this->suppressed_issues = $docblock_info['suppress']; $this->suppressed_issues = $docblock_info['suppress'];
self::$method_suppress[$method_id] = $this->suppressed_issues; self::$method_suppress[$method_id] = $this->suppressed_issues;
@ -329,7 +352,8 @@ class MethodChecker extends FunctionLikeChecker
$cased_method_id = $method_id; $cased_method_id = $method_id;
$method_parts = explode('::', $method_id); $method_parts = explode('::', $method_id);
$method_id = $method_parts[0] . '::' . strtolower($method_parts[1]); $method_parts[1] = strtolower($method_parts[1]);
$method_id = implode('::', $method_parts);
self::registerClassMethod($method_id); self::registerClassMethod($method_id);
@ -337,6 +361,10 @@ class MethodChecker extends FunctionLikeChecker
return true; return true;
} }
if ($method_parts[1] === '__construct') {
}
if (IssueBuffer::accepts( if (IssueBuffer::accepts(
new UndefinedMethod('Method ' . $cased_method_id . ' does not exist', $file_name, $line_number), new UndefinedMethod('Method ' . $cased_method_id . ' does not exist', $file_name, $line_number),
$suppresssed_issues $suppresssed_issues

View File

@ -3546,8 +3546,17 @@ class StatementsChecker
{ {
$function_params = null; $function_params = null;
$is_variadic = false;
if ($method_id) { if ($method_id) {
$function_params = FunctionLikeChecker::getParamsById($method_id, $args, $this->file_name); $function_params = FunctionLikeChecker::getParamsById($method_id, $args, $this->file_name);
if (strpos($method_id, '::')) {
$is_variadic = MethodChecker::isVariadic($method_id);
}
else {
$is_variadic = FunctionChecker::isVariadic(strtolower($method_id), $this->file_name);
}
} }
foreach ($args as $argument_offset => $arg) { foreach ($args as $argument_offset => $arg) {
@ -3640,7 +3649,10 @@ class StatementsChecker
} }
if ($method_id) { if ($method_id) {
if (count($args) > count($function_params) && (!count($function_params) || $function_params[count($function_params) - 1]->name !== '...=')) { if (!$is_variadic
&& count($args) > count($function_params)
&& (!count($function_params) || $function_params[count($function_params) - 1]->name !== '...=')
) {
if (IssueBuffer::accepts( if (IssueBuffer::accepts(
new TooManyArguments('Too many arguments for method ' . ($cased_method_id ?: $method_id), $this->checked_file_name, $line_number), new TooManyArguments('Too many arguments for method ' . ($cased_method_id ?: $method_id), $this->checked_file_name, $line_number),
$this->suppressed_issues $this->suppressed_issues