Skip to content

Commit

Permalink
add test
Browse files Browse the repository at this point in the history
  • Loading branch information
kkmuffme committed Feb 29, 2024
1 parent 5b7b6eb commit 2bdba92
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 0 deletions.
3 changes: 3 additions & 0 deletions tests/Config/Plugin/FunctionPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Psalm\Plugin\PluginEntryPointInterface;
use Psalm\Plugin\RegistrationInterface;
use Psalm\Test\Config\Plugin\Hook\CallUserFuncLikeParamsProvider;
use Psalm\Test\Config\Plugin\Hook\MagicFunctionProvider;
use SimpleXMLElement;

Expand All @@ -12,8 +13,10 @@ class FunctionPlugin implements PluginEntryPointInterface
{
public function __invoke(RegistrationInterface $registration, ?SimpleXMLElement $config = null): void
{
require_once __DIR__ . '/Hook/CallUserFuncLikeParamsProvider.php';
require_once __DIR__ . '/Hook/MagicFunctionProvider.php';

$registration->registerHooksFromClass(CallUserFuncLikeParamsProvider::class);
$registration->registerHooksFromClass(MagicFunctionProvider::class);
}
}
85 changes: 85 additions & 0 deletions tests/Config/Plugin/Hook/CallUserFuncLikeParamsProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace Psalm\Test\Config\Plugin\Hook;

use PhpParser;
use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Plugin\EventHandler\Event\FunctionParamsProviderEvent;
use Psalm\Plugin\EventHandler\FunctionParamsProviderInterface;
use Psalm\Storage\FunctionLikeParameter;
use Psalm\Type;
use Psalm\Type\Atomic\TCallable;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Union;

class CallUserFuncLikeParamsProvider implements
FunctionParamsProviderInterface
{
/**
* @return array<lowercase-string>
*/
public static function getFunctionIds(): array
{
return ['call_user_func_like'];
}

/**
* @return ?array<int, FunctionLikeParameter>
*/
public static function getFunctionParams(FunctionParamsProviderEvent $event): ?array
{
$statements_source = $event->getStatementsSource();
if (!$statements_source instanceof StatementsAnalyzer) {
return null;
}

$call_args = $event->getCallArgs();
if (!isset($call_args[0])) {
return null;
}

$function_call_arg = $call_args[0];

$mapping_function_ids = array();
if ($function_call_arg->value instanceof PhpParser\Node\Scalar\String_
|| $function_call_arg->value instanceof PhpParser\Node\Expr\Array_
|| $function_call_arg->value instanceof PhpParser\Node\Expr\BinaryOp\Concat
) {
$mapping_function_ids = CallAnalyzer::getFunctionIdsFromCallableArg(
$statements_source,
$function_call_arg->value,
);
}

if (!isset($mapping_function_ids[0])) {
return null;
}

$codebase = $event->getStatementsSource()->getCodebase();
$function_like_storage = $codebase->getFunctionLikeStorage($statements_source, $mapping_function_ids[0]);

$callback_param_types = [];
foreach ($function_like_storage->params as $function_like_parameter) {
$param_type_union = $function_like_parameter->type;
if (!$param_type_union) {
$param_type_union = Type::getMixed();
}

if ($function_like_parameter->is_nullable || $function_like_parameter->is_optional) {
$param_type_union = $param_type_union->setPossiblyUndefined(true);
}

$callback_param_types[] = $param_type_union;
}

$callback_params = new TKeyedArray(
$callback_param_types,

Check failure on line 77 in tests/Config/Plugin/Hook/CallUserFuncLikeParamsProvider.php

View workflow job for this annotation

GitHub Actions / build

ArgumentTypeCoercion

tests/Config/Plugin/Hook/CallUserFuncLikeParamsProvider.php:77:13: ArgumentTypeCoercion: Argument 1 of Psalm\Type\Atomic\TKeyedArray::__construct expects non-empty-array<int|string, Psalm\Type\Union>, but parent type list{0?: Psalm\Type\Union, ...<Psalm\Type\Union>} provided (see https://psalm.dev/193)
);

return array(
new FunctionLikeParameter('callable', false, new Union([new TCallable()]), null, null, null, false),
new FunctionLikeParameter('params', false, new Union([$callback_params]), null, null, null, false),
);
}
}
51 changes: 51 additions & 0 deletions tests/Config/PluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,57 @@ public function testFunctionProviderHooksInvalidArg(): void
$this->analyzeFile($file_path, new Context());
}

public function testFunctionProviderHooksThisClassInvalidArg(): void
{
$this->expectExceptionMessage('InvalidScalarArgument');
$this->expectException(CodeException::class);
require_once __DIR__ . '/Plugin/FunctionPlugin.php';

$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__, 2) . DIRECTORY_SEPARATOR,
'<?xml version="1.0"?>
<psalm
errorLevel="1"
>
<projectFiles>
<directory name="src" />
</projectFiles>
<plugins>
<pluginClass class="Psalm\\Test\\Config\\Plugin\\FunctionPlugin" />
</plugins>
</psalm>',
),
);

$this->project_analyzer->getCodebase()->config->initializePlugins($this->project_analyzer);

$file_path = getcwd() . '/src/somefile.php';

$this->addFile(
$file_path,
'<?php
namespace Foo\Acme;
/**
* @param callable $callable
* @param array $params
*/
function call_user_func_like($callable, $params): void {}
class Bar {
public function run(int $a): int {
return ($a + 2);
}
public function init(): void {
call_user_func_like(array($this, "run"), array("hello"));
}
}',
);

$this->analyzeFile($file_path, new Context());
}

public function testAfterAnalysisHooks(): void
{
require_once __DIR__ . '/Plugin/AfterAnalysisPlugin.php';
Expand Down

0 comments on commit 2bdba92

Please sign in to comment.