mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Merge pull request #10261 from robchett/allow_named_params_for_callables
Allow parameter names in callable docblocks with templates
This commit is contained in:
commit
67ac81e220
@ -235,6 +235,46 @@ class ParseTreeCreator
|
||||
$this->current_leaf = $new_parent_leaf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{0: string, 1: int, 2?: string} $current_token
|
||||
*/
|
||||
private function parseCallableParam(array $current_token, ParseTree $current_parent): void
|
||||
{
|
||||
$variadic = false;
|
||||
$has_default = false;
|
||||
|
||||
if ($current_token[0] === '&') {
|
||||
++$this->t;
|
||||
$current_token = $this->t < $this->type_token_count ? $this->type_tokens[$this->t] : null;
|
||||
} elseif ($current_token[0] === '...') {
|
||||
$variadic = true;
|
||||
|
||||
++$this->t;
|
||||
$current_token = $this->t < $this->type_token_count ? $this->type_tokens[$this->t] : null;
|
||||
} elseif ($current_token[0] === '=') {
|
||||
$has_default = true;
|
||||
|
||||
++$this->t;
|
||||
$current_token = $this->t < $this->type_token_count ? $this->type_tokens[$this->t] : null;
|
||||
}
|
||||
|
||||
if (!$current_token || $current_token[0][0] !== '$') {
|
||||
throw new TypeParseTreeException('Unexpected token after space');
|
||||
}
|
||||
|
||||
$new_leaf = new CallableParamTree($current_parent);
|
||||
$new_leaf->has_default = $has_default;
|
||||
$new_leaf->variadic = $variadic;
|
||||
|
||||
if ($current_parent !== $this->current_leaf) {
|
||||
$new_leaf->children = [$this->current_leaf];
|
||||
array_pop($current_parent->children);
|
||||
}
|
||||
$current_parent->children[] = $new_leaf;
|
||||
|
||||
$this->current_leaf = $new_leaf;
|
||||
}
|
||||
|
||||
private function handleLessThan(): void
|
||||
{
|
||||
if (!$this->current_leaf instanceof FieldEllipsis) {
|
||||
@ -553,24 +593,27 @@ class ParseTreeCreator
|
||||
|
||||
$current_parent = $this->current_leaf->parent;
|
||||
|
||||
if ($current_parent instanceof CallableTree) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ($current_parent && !$current_parent instanceof MethodTree) {
|
||||
//while ($current_parent && !$method_or_callable_parent) {
|
||||
while ($current_parent && !$current_parent instanceof MethodTree && !$current_parent instanceof CallableTree) {
|
||||
$this->current_leaf = $current_parent;
|
||||
$current_parent = $current_parent->parent;
|
||||
}
|
||||
|
||||
$next_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null;
|
||||
|
||||
if (!$current_parent instanceof MethodTree || !$next_token) {
|
||||
if (!($current_parent instanceof MethodTree || $current_parent instanceof CallableTree) || !$next_token) {
|
||||
throw new TypeParseTreeException('Unexpected space');
|
||||
}
|
||||
|
||||
++$this->t;
|
||||
|
||||
$this->createMethodParam($next_token, $current_parent);
|
||||
if ($current_parent instanceof MethodTree) {
|
||||
++$this->t;
|
||||
$this->createMethodParam($next_token, $current_parent);
|
||||
}
|
||||
if ($current_parent instanceof CallableTree) {
|
||||
++$this->t;
|
||||
$this->parseCallableParam($next_token, $current_parent);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleQuestionMark(): void
|
||||
|
@ -679,6 +679,68 @@ class TypeAnnotationTest extends TestCase
|
||||
'$output===' => 'callable():int',
|
||||
],
|
||||
],
|
||||
'callableFormats' => [
|
||||
'code' => '<?php
|
||||
/**
|
||||
* @psalm-type A callable(int, int): string
|
||||
* @psalm-type B callable(int, int=): string
|
||||
* @psalm-type C callable(int $a, string $b): void
|
||||
* @psalm-type D callable(string $c): mixed
|
||||
* @psalm-type E callable(string $c): mixed
|
||||
* @psalm-type F callable(float...): (int|null)
|
||||
* @psalm-type G callable(float ...$d): (int|null)
|
||||
* @psalm-type H callable(array<int>): array<string>
|
||||
* @psalm-type I callable(array<string, int> $e): array<int, string>
|
||||
* @psalm-type J callable(array<int> ...): string
|
||||
* @psalm-type K callable(array<int> ...$e): string
|
||||
* @psalm-type L \Closure(int, int): string
|
||||
*
|
||||
* @method ma(): A
|
||||
* @method mb(): B
|
||||
* @method mc(): C
|
||||
* @method md(): D
|
||||
* @method me(): E
|
||||
* @method mf(): F
|
||||
* @method mg(): G
|
||||
* @method mh(): H
|
||||
* @method mi(): I
|
||||
* @method mj(): J
|
||||
* @method mk(): K
|
||||
* @method ml(): L
|
||||
*/
|
||||
class Foo {
|
||||
public function __call(string $method, array $params) { return 1; }
|
||||
}
|
||||
|
||||
$foo = new \Foo();
|
||||
$output_ma = $foo->ma();
|
||||
$output_mb = $foo->mb();
|
||||
$output_mc = $foo->mc();
|
||||
$output_md = $foo->md();
|
||||
$output_me = $foo->me();
|
||||
$output_mf = $foo->mf();
|
||||
$output_mg = $foo->mg();
|
||||
$output_mh = $foo->mh();
|
||||
$output_mi = $foo->mi();
|
||||
$output_mj = $foo->mj();
|
||||
$output_mk = $foo->mk();
|
||||
$output_ml = $foo->ml();
|
||||
',
|
||||
'assertions' => [
|
||||
'$output_ma===' => 'callable(int, int):string',
|
||||
'$output_mb===' => 'callable(int, int=):string',
|
||||
'$output_mc===' => 'callable(int, string):void',
|
||||
'$output_md===' => 'callable(string):mixed',
|
||||
'$output_me===' => 'callable(string):mixed',
|
||||
'$output_mf===' => 'callable(float...):(int|null)',
|
||||
'$output_mg===' => 'callable(float...):(int|null)',
|
||||
'$output_mh===' => 'callable(array<array-key, int>):array<array-key, string>',
|
||||
'$output_mi===' => 'callable(array<string, int>):array<int, string>',
|
||||
'$output_mj===' => 'callable(array<array-key, int>...):string',
|
||||
'$output_mk===' => 'callable(array<array-key, int>...):string',
|
||||
'$output_ml===' => 'Closure(int, int):string',
|
||||
],
|
||||
],
|
||||
'unionOfStringsContainingBraceChar' => [
|
||||
'code' => '<?php
|
||||
/** @psalm-type T \'{\'|\'}\' */
|
||||
|
Loading…
Reference in New Issue
Block a user