Skip to content

Commit 9d45205

Browse files
authored
Allow omitting @param type
1 parent d579798 commit 9d45205

File tree

5 files changed

+158
-5
lines changed

5 files changed

+158
-5
lines changed

Diff for: doc/grammars/phpdoc-param.peg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PhpDocParam
2-
= AnnotationName Type IsReference? IsVariadic? ParameterName Description?
2+
= AnnotationName Type? IsReference? IsVariadic? ParameterName Description?
33

44
AnnotationName
55
= '@param'

Diff for: src/Ast/PhpDoc/PhpDocNode.php

+14
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,20 @@ static function (PhpDocTagValueNode $value): bool {
7676
}
7777

7878

79+
/**
80+
* @return TypelessParamTagValueNode[]
81+
*/
82+
public function getTypelessParamTagValues(string $tagName = '@param'): array
83+
{
84+
return array_filter(
85+
array_column($this->getTagsByName($tagName), 'value'),
86+
static function (PhpDocTagValueNode $value): bool {
87+
return $value instanceof TypelessParamTagValueNode;
88+
}
89+
);
90+
}
91+
92+
7993
/**
8094
* @return TemplateTagValueNode[]
8195
*/

Diff for: src/Ast/PhpDoc/TypelessParamTagValueNode.php

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
4+
5+
use PHPStan\PhpDocParser\Ast\NodeAttributes;
6+
use function trim;
7+
8+
class TypelessParamTagValueNode implements PhpDocTagValueNode
9+
{
10+
11+
use NodeAttributes;
12+
13+
/** @var bool */
14+
public $isReference;
15+
16+
/** @var bool */
17+
public $isVariadic;
18+
19+
/** @var string */
20+
public $parameterName;
21+
22+
/** @var string (may be empty) */
23+
public $description;
24+
25+
public function __construct(bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
26+
{
27+
$this->isReference = $isReference;
28+
$this->isVariadic = $isVariadic;
29+
$this->parameterName = $parameterName;
30+
$this->description = $description;
31+
}
32+
33+
34+
public function __toString(): string
35+
{
36+
$reference = $this->isReference ? '&' : '';
37+
$variadic = $this->isVariadic ? '...' : '';
38+
return trim("{$reference}{$variadic}{$this->parameterName} {$this->description}");
39+
}
40+
41+
}

Diff for: src/Parser/PhpDocParser.php

+20-4
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,31 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
230230
}
231231

232232

233-
private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamTagValueNode
233+
/**
234+
* @return Ast\PhpDoc\ParamTagValueNode|Ast\PhpDoc\TypelessParamTagValueNode
235+
*/
236+
private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode
234237
{
235-
$type = $this->typeParser->parse($tokens);
238+
if (
239+
$tokens->isCurrentTokenType(Lexer::TOKEN_REFERENCE)
240+
|| $tokens->isCurrentTokenType(Lexer::TOKEN_VARIADIC)
241+
|| $tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)
242+
) {
243+
$type = null;
244+
} else {
245+
$type = $this->typeParser->parse($tokens);
246+
}
247+
236248
$isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE);
237249
$isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC);
238250
$parameterName = $this->parseRequiredVariableName($tokens);
239251
$description = $this->parseOptionalDescription($tokens);
240-
return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description, $isReference);
252+
253+
if ($type !== null) {
254+
return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description, $isReference);
255+
}
256+
257+
return new Ast\PhpDoc\TypelessParamTagValueNode($isVariadic, $parameterName, $description, $isReference);
241258
}
242259

243260

@@ -463,7 +480,6 @@ private function parseRequiredVariableName(TokenIterator $tokens): string
463480
return $parameterName;
464481
}
465482

466-
467483
private function parseOptionalDescription(TokenIterator $tokens, bool $limitStartToken = false): string
468484
{
469485
if ($limitStartToken) {

Diff for: tests/PHPStan/Parser/PhpDocParserTest.php

+82
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
2828
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode;
2929
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasTagValueNode;
30+
use PHPStan\PhpDocParser\Ast\PhpDoc\TypelessParamTagValueNode;
3031
use PHPStan\PhpDocParser\Ast\PhpDoc\UsesTagValueNode;
3132
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
3233
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
@@ -64,6 +65,7 @@ protected function setUp(): void
6465
/**
6566
* @dataProvider provideTagsWithNumbers
6667
* @dataProvider provideParamTagsData
68+
* @dataProvider provideTypelessParamTagsData
6769
* @dataProvider provideVarTagsData
6870
* @dataProvider provideReturnTagsData
6971
* @dataProvider provideThrowsTagsData
@@ -395,6 +397,86 @@ public function provideParamTagsData(): Iterator
395397
];
396398
}
397399

400+
public function provideTypelessParamTagsData(): Iterator
401+
{
402+
yield [
403+
'OK',
404+
'/** @param $foo description */',
405+
new PhpDocNode([
406+
new PhpDocTagNode(
407+
'@param',
408+
new TypelessParamTagValueNode(
409+
false,
410+
'$foo',
411+
'description'
412+
)
413+
),
414+
]),
415+
];
416+
417+
yield [
418+
'OK reference',
419+
'/** @param &$foo description */',
420+
new PhpDocNode([
421+
new PhpDocTagNode(
422+
'@param',
423+
new TypelessParamTagValueNode(
424+
false,
425+
'$foo',
426+
'description',
427+
true
428+
)
429+
),
430+
]),
431+
];
432+
433+
yield [
434+
'OK variadic',
435+
'/** @param ...$foo description */',
436+
new PhpDocNode([
437+
new PhpDocTagNode(
438+
'@param',
439+
new TypelessParamTagValueNode(
440+
true,
441+
'$foo',
442+
'description'
443+
)
444+
),
445+
]),
446+
];
447+
448+
yield [
449+
'OK reference variadic',
450+
'/** @param &...$foo description */',
451+
new PhpDocNode([
452+
new PhpDocTagNode(
453+
'@param',
454+
new TypelessParamTagValueNode(
455+
true,
456+
'$foo',
457+
'description',
458+
true
459+
)
460+
),
461+
]),
462+
];
463+
464+
yield [
465+
'OK without type and description',
466+
'/** @param $foo */',
467+
new PhpDocNode([
468+
new PhpDocTagNode(
469+
'@param',
470+
new TypelessParamTagValueNode(
471+
false,
472+
'$foo',
473+
'',
474+
false
475+
)
476+
),
477+
]),
478+
];
479+
}
398480

399481
public function provideVarTagsData(): Iterator
400482
{

0 commit comments

Comments
 (0)