Skip to content

Commit

Permalink
Allow omitting @param type
Browse files Browse the repository at this point in the history
  • Loading branch information
rvanvelzen committed Jun 8, 2022
1 parent b75949e commit 66ae696
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 5 deletions.
1 change: 1 addition & 0 deletions doc/grammars/phpdoc-param.peg
@@ -1,5 +1,6 @@
PhpDocParam
= AnnotationName Type IsReference? IsVariadic? ParameterName Description?
/ AnnotationName Type? IsReference? IsVariadic? ParameterName Description

AnnotationName
= '@param'
Expand Down
7 changes: 4 additions & 3 deletions src/Ast/PhpDoc/ParamTagValueNode.php
Expand Up @@ -11,7 +11,7 @@ class ParamTagValueNode implements PhpDocTagValueNode

use NodeAttributes;

/** @var TypeNode */
/** @var TypeNode|null */
public $type;

/** @var bool */
Expand All @@ -26,7 +26,7 @@ class ParamTagValueNode implements PhpDocTagValueNode
/** @var string (may be empty) */
public $description;

public function __construct(TypeNode $type, bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
public function __construct(?TypeNode $type, bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
{
$this->type = $type;
$this->isReference = $isReference;
Expand All @@ -38,9 +38,10 @@ public function __construct(TypeNode $type, bool $isVariadic, string $parameterN

public function __toString(): string
{
$type = $this->type !== null ? "{$this->type} " : '';
$reference = $this->isReference ? '&' : '';
$variadic = $this->isVariadic ? '...' : '';
return trim("{$this->type} {$reference}{$variadic}{$this->parameterName} {$this->description}");
return trim("{$type}{$reference}{$variadic}{$this->parameterName} {$this->description}");
}

}
30 changes: 28 additions & 2 deletions src/Parser/PhpDocParser.php
Expand Up @@ -8,6 +8,7 @@
use PHPStan\ShouldNotHappenException;
use function array_values;
use function count;
use function strlen;
use function trim;

class PhpDocParser
Expand Down Expand Up @@ -232,11 +233,20 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph

private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamTagValueNode
{
$type = $this->typeParser->parse($tokens);
if (
$tokens->isCurrentTokenType(Lexer::TOKEN_REFERENCE)
|| $tokens->isCurrentTokenType(Lexer::TOKEN_VARIADIC)
|| $tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)
) {
$type = null;
} else {
$type = $this->typeParser->parse($tokens);
}

$isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE);
$isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC);
$parameterName = $this->parseRequiredVariableName($tokens);
$description = $this->parseOptionalDescription($tokens);
$description = $type === null ? $this->parseRequiredDescription($tokens) : $this->parseOptionalDescription($tokens);
return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description, $isReference);
}

Expand Down Expand Up @@ -463,6 +473,22 @@ private function parseRequiredVariableName(TokenIterator $tokens): string
return $parameterName;
}

private function parseRequiredDescription(TokenIterator $tokens): string
{
$tokens->pushSavePoint();

$description = $this->parseOptionalDescription($tokens);

if (strlen($description) === 0) {
$tokens->rollback();

$tokens->consumeTokenType(Lexer::TOKEN_OTHER);
}

$tokens->dropSavePoint();

return $description;
}

private function parseOptionalDescription(TokenIterator $tokens, bool $limitStartToken = false): string
{
Expand Down
85 changes: 85 additions & 0 deletions tests/PHPStan/Parser/PhpDocParserTest.php
Expand Up @@ -242,6 +242,72 @@ public function provideParamTagsData(): Iterator
]),
];

yield [
'OK without type',
'/** @param $foo description */',
new PhpDocNode([
new PhpDocTagNode(
'@param',
new ParamTagValueNode(
null,
false,
'$foo',
'description'
)
),
]),
];

yield [
'OK reference without type',
'/** @param &$foo description */',
new PhpDocNode([
new PhpDocTagNode(
'@param',
new ParamTagValueNode(
null,
false,
'$foo',
'description',
true
)
),
]),
];

yield [
'OK variadic without type',
'/** @param ...$foo description */',
new PhpDocNode([
new PhpDocTagNode(
'@param',
new ParamTagValueNode(
null,
true,
'$foo',
'description'
)
),
]),
];

yield [
'OK reference variadic without type',
'/** @param &...$foo description */',
new PhpDocNode([
new PhpDocTagNode(
'@param',
new ParamTagValueNode(
null,
true,
'$foo',
'description',
true
)
),
]),
];

yield [
'invalid without type, parameter name and description',
'/** @param */',
Expand Down Expand Up @@ -393,6 +459,25 @@ public function provideParamTagsData(): Iterator
),
]),
];

yield [
'invalid without type and description',
'/** @param $foo */',
new PhpDocNode([
new PhpDocTagNode(
'@param',
new InvalidTagValueNode(
'$foo',
new ParserException(
'*/',
Lexer::TOKEN_CLOSE_PHPDOC,
16,
Lexer::TOKEN_OTHER
)
)
),
]),
];
}


Expand Down

0 comments on commit 66ae696

Please sign in to comment.