mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 13:51:54 +01:00
Merge pull request #8144 from SamMousa/feature-check-unused-callmap-ignore
Callmap validation ignore list cleanup
This commit is contained in:
commit
1f87a5056e
@ -113,6 +113,7 @@
|
||||
"lint": "parallel-lint ./src ./tests",
|
||||
"phpunit": "paratest --runner=WrapperRunner",
|
||||
"phpunit-std": "phpunit",
|
||||
"verify-callmap": "phpunit tests/Internal/Codebase/InternalCallMapHandlerTest.php",
|
||||
"psalm": "@php ./psalm --find-dead-code",
|
||||
"tests": [
|
||||
"@lint",
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
namespace Psalm\Tests\Internal\Codebase;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
use PHPUnit\Framework\SkippedTestError;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\Internal\Analyzer\ProjectAnalyzer;
|
||||
use Psalm\Internal\Codebase\InternalCallMapHandler;
|
||||
@ -16,135 +19,370 @@ use Psalm\Type;
|
||||
use ReflectionFunction;
|
||||
use ReflectionParameter;
|
||||
use ReflectionType;
|
||||
use Throwable;
|
||||
|
||||
use function class_exists;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function function_exists;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function json_encode;
|
||||
use function preg_match;
|
||||
use function print_r;
|
||||
use function strcmp;
|
||||
use function strncmp;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
|
||||
use const PHP_MAJOR_VERSION;
|
||||
use const PHP_MINOR_VERSION;
|
||||
|
||||
class InternalCallMapHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
* Specify a function name as value, or a function name as key and
|
||||
* an array containing the PHP versions in which to ignore this function as values.
|
||||
* @var array<int|string, string|list<string>>
|
||||
*/
|
||||
private static $ignoredFunctions = [
|
||||
'sprintf', 'printf', 'ctype_print', 'date_sunrise' /** deprecated in 8.1 */,
|
||||
'file_put_contents',
|
||||
'dom_import_simplexml', 'imagegd', 'imagegd2', 'mysqli_execute', 'array_multisort',
|
||||
'intlcal_from_date_time', 'simplexml_import_dom', 'imagefilledpolygon',
|
||||
/** deprecated in 8.0 */
|
||||
'zip_entry_close',
|
||||
'date_time_set',
|
||||
'curl_unescape',
|
||||
'extract',
|
||||
'enum_exists',
|
||||
'igbinary_unserialize',
|
||||
'count',
|
||||
'lzf_compress',
|
||||
'long2ip',
|
||||
'apcu_entry',
|
||||
'array_column',
|
||||
'preg_replace_callback_array',
|
||||
'preg_filter',
|
||||
'zlib_encode',
|
||||
'inotify_rm_watch',
|
||||
'mail',
|
||||
'easter_date',
|
||||
'date_isodate_set',
|
||||
'snmpset',
|
||||
'get_class_methods',
|
||||
'filter_var_array',
|
||||
'deflate_add',
|
||||
'array_diff',
|
||||
'array_diff_assoc',
|
||||
'array_diff_key',
|
||||
'array_intersect',
|
||||
'array_intersect_assoc',
|
||||
'array_intersect_key',
|
||||
'array_key_exists',
|
||||
'array_merge',
|
||||
'array_merge_recursive',
|
||||
'array_multisort',
|
||||
'array_push',
|
||||
'array_replace',
|
||||
'array_replace_recursive',
|
||||
'array_unshift',
|
||||
'bcdiv',
|
||||
'bcmod',
|
||||
'bcpowmod',
|
||||
'bzdecompress',
|
||||
'substr_replace',
|
||||
'lzf_decompress',
|
||||
'mongodb\bson\tophp',
|
||||
'fputcsv',
|
||||
'count',
|
||||
'crypt',
|
||||
'date_isodate_set',
|
||||
'datefmt_create',
|
||||
'datefmt_get_timezone',
|
||||
'datefmt_localtime',
|
||||
'datefmt_parse',
|
||||
'datefmt_set_timezone',
|
||||
'debug_zval_dump',
|
||||
'deflate_add',
|
||||
'dns_get_mx',
|
||||
'easter_date',
|
||||
'enchant_broker_describe',
|
||||
'enchant_broker_dict_exists',
|
||||
'enchant_broker_free',
|
||||
'enchant_broker_free_dict',
|
||||
'enchant_broker_get_dict_path',
|
||||
'enchant_broker_get_error',
|
||||
'enchant_broker_list_dicts',
|
||||
'enchant_broker_request_dict',
|
||||
'enchant_broker_request_pwl_dict',
|
||||
'enchant_broker_set_dict_path',
|
||||
'enchant_broker_set_ordering',
|
||||
'enchant_dict_add_to_personal',
|
||||
'enchant_dict_add_to_session',
|
||||
'enchant_dict_check',
|
||||
'enchant_dict_describe',
|
||||
'enchant_dict_get_error',
|
||||
'enchant_dict_is_in_session',
|
||||
'enchant_dict_quick_check',
|
||||
'enchant_dict_store_replacement',
|
||||
'enchant_dict_suggest',
|
||||
'enum_exists',
|
||||
'extract',
|
||||
'filter_var',
|
||||
'filter_var_array',
|
||||
// https://www.php.net/manual/en/function.fputcsv.php
|
||||
'fputcsv' => ['8.1'],
|
||||
'get_class_methods',
|
||||
'get_headers',
|
||||
'get_parent_class',
|
||||
'filter_var',
|
||||
'array_key_exists',
|
||||
'gmp_clrbit',
|
||||
'gmp_div',
|
||||
'gmp_setbit',
|
||||
'gnupg_adddecryptkey',
|
||||
'gnupg_addencryptkey',
|
||||
'gnupg_addsignkey',
|
||||
'gnupg_cleardecryptkeys',
|
||||
'gnupg_clearencryptkeys',
|
||||
'gnupg_clearsignkeys',
|
||||
'gnupg_decrypt',
|
||||
'gnupg_decryptverify',
|
||||
'gnupg_encrypt',
|
||||
'gnupg_encryptsign',
|
||||
'gnupg_export',
|
||||
'gnupg_geterror',
|
||||
'gnupg_getprotocol',
|
||||
'gnupg_import',
|
||||
'gnupg_init',
|
||||
'gnupg_keyinfo',
|
||||
'gnupg_setarmor',
|
||||
'gnupg_seterrormode',
|
||||
'gnupg_setsignmode',
|
||||
'gnupg_sign',
|
||||
'gnupg_verify',
|
||||
'hash_hmac_file',
|
||||
'igbinary_unserialize',
|
||||
'imagefilledpolygon',
|
||||
'imagefilter',
|
||||
'imagegd',
|
||||
'imagegd2',
|
||||
'imageinterlace',
|
||||
'imageopenpolygon',
|
||||
'imagepolygon',
|
||||
'imagerotate',
|
||||
'imagesetinterpolation',
|
||||
'imagettfbbox',
|
||||
'imagettftext',
|
||||
'imagexbm',
|
||||
'imap_delete',
|
||||
'imap_open',
|
||||
'imap_rfc822_write_address',
|
||||
'imap_sort',
|
||||
'imap_undelete',
|
||||
'inflate_add',
|
||||
'inflate_get_read_len',
|
||||
'inflate_get_status',
|
||||
'inotify_rm_watch',
|
||||
'intlcal_from_date_time',
|
||||
'intlcal_get_weekend_transition',
|
||||
'intlgregcal_create_instance',
|
||||
'intlgregcal_is_leap_year',
|
||||
'intltz_create_enumeration',
|
||||
'intltz_get_canonical_id',
|
||||
'intltz_get_display_name',
|
||||
'ldap_compare',
|
||||
'ldap_delete',
|
||||
'ldap_exop',
|
||||
'ldap_free_result',
|
||||
'ldap_get_option',
|
||||
'ldap_list',
|
||||
'ldap_mod_add',
|
||||
'ldap_mod_del',
|
||||
'ldap_mod_replace',
|
||||
'ldap_modify',
|
||||
'ldap_modify_batch',
|
||||
'ldap_next_entry',
|
||||
'ldap_parse_reference',
|
||||
'ldap_read',
|
||||
'ldap_rename',
|
||||
'ldap_search',
|
||||
'ldap_start_tls',
|
||||
'long2ip',
|
||||
'lzf_compress',
|
||||
'lzf_decompress',
|
||||
'mail',
|
||||
'mailparse_msg_extract_part',
|
||||
'mailparse_msg_extract_part_file',
|
||||
'mailparse_msg_extract_whole_part_file',
|
||||
'mailparse_msg_free',
|
||||
'mailparse_msg_get_part',
|
||||
'mailparse_msg_get_part_data',
|
||||
'mailparse_msg_get_structure',
|
||||
'mailparse_msg_parse',
|
||||
'mailparse_stream_encode',
|
||||
'memcache_add',
|
||||
'memcache_add_server',
|
||||
'memcache_append',
|
||||
'memcache_cas',
|
||||
'memcache_close',
|
||||
'memcache_connect',
|
||||
'memcache_decrement',
|
||||
'memcache_delete',
|
||||
'memcache_flush',
|
||||
'memcache_get_extended_stats',
|
||||
'memcache_get_server_status',
|
||||
'memcache_get_stats',
|
||||
'memcache_get_version',
|
||||
'memcache_increment',
|
||||
'memcache_pconnect',
|
||||
'memcache_prepend',
|
||||
'memcache_replace',
|
||||
'memcache_set',
|
||||
'memcache_set_compress_threshold',
|
||||
'memcache_set_failure_callback',
|
||||
'memcache_set_server_params',
|
||||
'mongodb\bson\tophp',
|
||||
'msg_receive',
|
||||
'msg_remove_queue',
|
||||
'msg_send',
|
||||
'msg_set_queue',
|
||||
'msg_stat_queue',
|
||||
'mysqli_poll',
|
||||
'mysqli_real_connect',
|
||||
'mysqli_stmt_bind_param',
|
||||
'normalizer_get_raw_decomposition',
|
||||
'oauth_get_sbs',
|
||||
'oci_collection_append',
|
||||
'oci_collection_assign',
|
||||
'oci_collection_element_assign',
|
||||
'oci_collection_element_get',
|
||||
'oci_collection_max',
|
||||
'oci_collection_size',
|
||||
'oci_collection_trim',
|
||||
'oci_fetch_object',
|
||||
'oci_field_is_null',
|
||||
'oci_field_name',
|
||||
'oci_field_precision',
|
||||
'oci_field_scale',
|
||||
'oci_field_size',
|
||||
'oci_field_type',
|
||||
'oci_field_type_raw',
|
||||
'oci_free_collection',
|
||||
'oci_free_descriptor',
|
||||
'oci_lob_append',
|
||||
'oci_lob_eof',
|
||||
'oci_lob_erase',
|
||||
'oci_lob_export',
|
||||
'oci_lob_flush',
|
||||
'oci_lob_import',
|
||||
'oci_lob_load',
|
||||
'oci_lob_read',
|
||||
'oci_lob_rewind',
|
||||
'oci_lob_save',
|
||||
'oci_lob_seek',
|
||||
'oci_lob_size',
|
||||
'oci_lob_tell',
|
||||
'oci_lob_truncate',
|
||||
'oci_lob_write',
|
||||
'oci_register_taf_callback',
|
||||
'oci_result',
|
||||
'ocigetbufferinglob',
|
||||
'ocisetbufferinglob',
|
||||
'odbc_procedurecolumns',
|
||||
'odbc_procedures',
|
||||
'odbc_result',
|
||||
'openssl_pkcs7_read',
|
||||
'pg_exec',
|
||||
'pg_fetch_all',
|
||||
'pg_get_notify',
|
||||
'pg_get_result',
|
||||
'pg_pconnect',
|
||||
'pg_select',
|
||||
'pg_send_execute',
|
||||
'preg_filter',
|
||||
'preg_replace_callback_array',
|
||||
'sapi_windows_cp_get',
|
||||
'stream_select',
|
||||
'hash_hmac_file'
|
||||
|
||||
'sem_acquire',
|
||||
'sem_get',
|
||||
'sem_release',
|
||||
'sem_remove',
|
||||
'shm_detach',
|
||||
'shm_get_var',
|
||||
'shm_has_var',
|
||||
'shm_put_var',
|
||||
'shm_remove',
|
||||
'shm_remove_var',
|
||||
'shmop_close',
|
||||
'shmop_delete',
|
||||
'shmop_read',
|
||||
'shmop_size',
|
||||
'shmop_write',
|
||||
'snmp_set_enum_print',
|
||||
'snmp_set_valueretrieval',
|
||||
'snmpset',
|
||||
'socket_addrinfo_lookup',
|
||||
'socket_bind',
|
||||
'socket_cmsg_space',
|
||||
'socket_connect',
|
||||
'socket_create_pair',
|
||||
'socket_get_option',
|
||||
'socket_getopt',
|
||||
'socket_getpeername',
|
||||
'socket_getsockname',
|
||||
'socket_read',
|
||||
'socket_recv',
|
||||
'socket_recvfrom',
|
||||
'socket_recvmsg',
|
||||
'socket_select',
|
||||
'socket_send',
|
||||
'socket_sendmsg',
|
||||
'socket_sendto',
|
||||
'socket_set_blocking',
|
||||
'socket_set_option',
|
||||
'socket_setopt',
|
||||
'socket_shutdown',
|
||||
'socket_strerror',
|
||||
'sodium_crypto_generichash',
|
||||
'sodium_crypto_generichash_final',
|
||||
'sodium_crypto_generichash_init',
|
||||
'sodium_crypto_generichash_update',
|
||||
'sodium_crypto_kx_client_session_keys',
|
||||
'sodium_crypto_secretstream_xchacha20poly1305_rekey',
|
||||
'sqlsrv_connect',
|
||||
'sqlsrv_errors',
|
||||
'sqlsrv_fetch_array',
|
||||
'sqlsrv_fetch_object',
|
||||
'sqlsrv_get_field',
|
||||
'sqlsrv_prepare',
|
||||
'sqlsrv_query',
|
||||
'sqlsrv_server_info',
|
||||
'stomp_abort',
|
||||
'stomp_ack',
|
||||
'stomp_begin',
|
||||
'stomp_commit',
|
||||
'stomp_read_frame',
|
||||
'stomp_send',
|
||||
'stomp_set_read_timeout',
|
||||
'stomp_subscribe',
|
||||
'stomp_unsubscribe',
|
||||
'stream_select' => ['8.0'],
|
||||
'substr_replace',
|
||||
'tidy_getopt',
|
||||
'uopz_allow_exit',
|
||||
'uopz_get_mock',
|
||||
'uopz_get_property',
|
||||
'uopz_get_return',
|
||||
'uopz_get_static',
|
||||
'uopz_set_mock',
|
||||
'uopz_set_property',
|
||||
'uopz_set_static',
|
||||
'uopz_unset_mock',
|
||||
'xdiff_file_bdiff',
|
||||
'xdiff_file_bdiff_size',
|
||||
'xdiff_file_diff',
|
||||
'xdiff_file_diff_binary',
|
||||
'xdiff_file_merge3',
|
||||
'xdiff_file_rabdiff',
|
||||
'xdiff_string_bdiff',
|
||||
'xdiff_string_bdiff_size',
|
||||
'xdiff_string_bpatch',
|
||||
'xdiff_string_diff',
|
||||
'xdiff_string_diff_binary',
|
||||
'xdiff_string_merge3',
|
||||
'xdiff_string_patch',
|
||||
'xdiff_string_patch_binary',
|
||||
'xdiff_string_rabdiff',
|
||||
'xmlrpc_server_add_introspection_data',
|
||||
'xmlrpc_server_call_method',
|
||||
'xmlrpc_server_destroy',
|
||||
'xmlrpc_server_register_introspection_callback',
|
||||
'xmlrpc_server_register_method',
|
||||
'yaml_emit',
|
||||
'yaml_emit_file',
|
||||
'zip_entry_close',
|
||||
'zlib_encode',
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private static $ignoredPrefixes = [
|
||||
'apcu_',
|
||||
'bc',
|
||||
'collator_',
|
||||
'ctype_',
|
||||
'datefmt_',
|
||||
'enchant_',
|
||||
'gmp_',
|
||||
'gnupg_',
|
||||
'image',
|
||||
'imap_',
|
||||
'inflate_',
|
||||
'intl',
|
||||
'ldap_',
|
||||
'mailparse_',
|
||||
'memcache_',
|
||||
'msg_',
|
||||
'mysqli_',
|
||||
'normalizer_',
|
||||
'oauth_',
|
||||
'oci',
|
||||
'odbc_',
|
||||
'openssl_',
|
||||
'pg_',
|
||||
'sem_',
|
||||
'shm_',
|
||||
'shmop_',
|
||||
'snmp_',
|
||||
'socket_',
|
||||
'sodium_',
|
||||
'sqlsrv_',
|
||||
'tidy_',
|
||||
'transliterator_',
|
||||
'uopz_',
|
||||
'xdiff_',
|
||||
'xmlrpc_server',
|
||||
'yaml_',
|
||||
];
|
||||
|
||||
/**
|
||||
* Initialized in setup
|
||||
* @var string Regex
|
||||
*/
|
||||
private static $prefixRegex = '//';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool whether to skip params for which no definition can be found in the callMap
|
||||
*/
|
||||
private $skipUndefinedParams = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Codebase
|
||||
*/
|
||||
private static $codebase;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
self::$prefixRegex = '/^(' . implode('|', self::$ignoredPrefixes) . ')/';
|
||||
$project_analyzer = new ProjectAnalyzer(
|
||||
new TestConfig(),
|
||||
new Providers(
|
||||
@ -155,6 +393,19 @@ class InternalCallMapHandlerTest extends TestCase
|
||||
self::$codebase = $project_analyzer->getCodebase();
|
||||
}
|
||||
|
||||
|
||||
public function testIgnoresAreSortedAndUnique(): void
|
||||
{
|
||||
$previousFunction = "";
|
||||
foreach (self::$ignoredFunctions as $key => $value) {
|
||||
/** @var string */
|
||||
$function = is_int($key) ? $value : $key;
|
||||
|
||||
$this->assertGreaterThan(0, strcmp($function, $previousFunction));
|
||||
$previousFunction = $function;
|
||||
}
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
self::$codebase = null;
|
||||
@ -179,7 +430,7 @@ class InternalCallMapHandlerTest extends TestCase
|
||||
|
||||
/**
|
||||
*
|
||||
* @return iterable<string, array{0: string, 1: array<int|string>}>
|
||||
* @return iterable<string, array{0: callable-string, 1: array<int|string>}>
|
||||
*/
|
||||
public function callMapEntryProvider(): iterable
|
||||
{
|
||||
@ -208,25 +459,78 @@ class InternalCallMapHandlerTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
private function isIgnored(string $functionName): bool
|
||||
{
|
||||
if (in_array($functionName, self::$ignoredFunctions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset(self::$ignoredFunctions[$functionName])
|
||||
&& is_array(self::$ignoredFunctions[$functionName])
|
||||
&& in_array(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, self::$ignoredFunctions[$functionName])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testIgnoresAreSortedAndUnique
|
||||
* @depends testGetcallmapReturnsAValidCallmap
|
||||
* @dataProvider callMapEntryProvider
|
||||
* @coversNothing
|
||||
* @psalm-param callable-string $functionName
|
||||
*/
|
||||
public function testIgnoredFunctionsStillFail(string $functionName, array $callMapEntry): void
|
||||
{
|
||||
if (!$this->isIgnored($functionName)) {
|
||||
// Dummy assertion to mark it as passed
|
||||
$this->assertTrue(true);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->expectException(ExpectationFailedException::class);
|
||||
|
||||
try {
|
||||
unset($callMapEntry[0]);
|
||||
/** @var array<string, string> $callMapEntry */
|
||||
$this->assertEntryIsCorrect($callMapEntry, $functionName);
|
||||
} catch (InvalidArgumentException $t) {
|
||||
// Silence this one for now.
|
||||
$this->markTestSkipped('IA');
|
||||
} catch (SkippedTestError $t) {
|
||||
die('this should not happen');
|
||||
} catch (ExpectationFailedException $e) {
|
||||
// This is good!
|
||||
throw $e;
|
||||
} catch (InvalidArgumentException $e) {
|
||||
// This can happen if a class does not exist, we handle the message to check for this case.
|
||||
if (preg_match('/^Could not get class storage for (.*)$/', $e->getMessage(), $matches)
|
||||
&& !class_exists($matches[1])
|
||||
) {
|
||||
die("Class mentioned in callmap does not exist: " . $matches[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->markTestIncomplete("Remove function '{$functionName}' from your ignores");
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will test functions that are in the callmap AND currently defined
|
||||
* @coversNothing
|
||||
* @depends testGetcallmapReturnsAValidCallmap
|
||||
* @depends testIgnoresAreSortedAndUnique
|
||||
* @dataProvider callMapEntryProvider
|
||||
* @psalm-param callable-string $functionName
|
||||
* @param array $callMapEntry
|
||||
*/
|
||||
public function testCallMapCompliesWithReflection(string $functionName, array $callMapEntry): void
|
||||
{
|
||||
/** @psalm-assert callable-string $functionName */
|
||||
if (!function_exists($functionName)) {
|
||||
$this->markTestSkipped("Function $functionName does not exist");
|
||||
}
|
||||
if (in_array($functionName, self::$ignoredFunctions)) {
|
||||
if ($this->isIgnored($functionName)) {
|
||||
$this->markTestSkipped("Function $functionName is ignored in config");
|
||||
}
|
||||
if (preg_match(self::$prefixRegex, $functionName)) {
|
||||
$this->markTestSkipped("Function $functionName has ignored prefix");
|
||||
}
|
||||
|
||||
unset($callMapEntry[0]);
|
||||
/** @var array<string, string> $callMapEntry */
|
||||
@ -291,11 +595,7 @@ class InternalCallMapHandlerTest extends TestCase
|
||||
$normalizedEntries[$normalizedKey] = $normalizedEntry;
|
||||
}
|
||||
foreach ($rF->getParameters() as $parameter) {
|
||||
if ($this->skipUndefinedParams && !isset($normalizedEntries[$parameter->getName()])) {
|
||||
continue;
|
||||
} else {
|
||||
$this->assertArrayHasKey($parameter->getName(), $normalizedEntries, "Callmap is missing entry for param {$parameter->getName()} in $functionName: " . print_r($normalizedEntries, true));
|
||||
}
|
||||
$this->assertArrayHasKey($parameter->getName(), $normalizedEntries, "Callmap is missing entry for param {$parameter->getName()} in $functionName: " . print_r($normalizedEntries, true));
|
||||
$this->assertParameter($normalizedEntries[$parameter->getName()], $parameter);
|
||||
}
|
||||
}
|
||||
@ -307,18 +607,13 @@ class InternalCallMapHandlerTest extends TestCase
|
||||
private function assertParameter(array $normalizedEntry, ReflectionParameter $param): void
|
||||
{
|
||||
$name = $param->getName();
|
||||
// $identifier = "Param $functionName - $name";
|
||||
try {
|
||||
$this->assertSame($param->isOptional(), $normalizedEntry['optional'], "Expected param '{$name}' to " . ($param->isOptional() ? "be" : "not be") . " optional");
|
||||
$this->assertSame($param->isVariadic(), $normalizedEntry['variadic'], "Expected param '{$name}' to " . ($param->isVariadic() ? "be" : "not be") . " variadic");
|
||||
$this->assertSame($param->isPassedByReference(), $normalizedEntry['byRef'], "Expected param '{$name}' to " . ($param->isPassedByReference() ? "be" : "not be") . " by reference");
|
||||
} catch (Throwable $t) {
|
||||
$this->markTestSkipped("Exception: " . $t->getMessage());
|
||||
}
|
||||
$this->assertSame($param->isOptional(), $normalizedEntry['optional'], "Expected param '{$name}' to " . ($param->isOptional() ? "be" : "not be") . " optional");
|
||||
$this->assertSame($param->isVariadic(), $normalizedEntry['variadic'], "Expected param '{$name}' to " . ($param->isVariadic() ? "be" : "not be") . " variadic");
|
||||
$this->assertSame($param->isPassedByReference(), $normalizedEntry['byRef'], "Expected param '{$name}' to " . ($param->isPassedByReference() ? "be" : "not be") . " by reference");
|
||||
|
||||
$expectedType = $param->getType();
|
||||
|
||||
if (isset($expectedType)) {
|
||||
if (isset($expectedType) && !empty($normalizedEntry['type'])) {
|
||||
$this->assertTypeValidity($expectedType, $normalizedEntry['type'], "Param '{$name}' has incorrect type");
|
||||
}
|
||||
}
|
||||
@ -329,8 +624,17 @@ class InternalCallMapHandlerTest extends TestCase
|
||||
private function assertTypeValidity(ReflectionType $reflected, string $specified, string $message): void
|
||||
{
|
||||
$expectedType = Reflection::getPsalmTypeFromReflectionType($reflected);
|
||||
|
||||
$parsedType = Type::parseString($specified);
|
||||
|
||||
$this->assertTrue(UnionTypeComparator::isContainedBy(self::$codebase, $parsedType, $expectedType), $message);
|
||||
try {
|
||||
$this->assertTrue(UnionTypeComparator::isContainedBy(self::$codebase, $parsedType, $expectedType), $message);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
if (preg_match('/^Could not get class storage for (.*)$/', $e->getMessage(), $matches)
|
||||
&& !class_exists($matches[1])
|
||||
) {
|
||||
die("Class mentioned in callmap does not exist: " . $matches[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user