Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow omitting @param type #127

Merged
merged 6 commits into from Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/grammars/phpdoc-param.peg
@@ -1,5 +1,5 @@
PhpDocParam
= AnnotationName Type IsReference? IsVariadic? ParameterName Description?
= AnnotationName Type? IsReference? IsVariadic? ParameterName Description?

AnnotationName
= '@param'
Expand Down
14 changes: 14 additions & 0 deletions src/Ast/PhpDoc/PhpDocNode.php
Expand Up @@ -76,6 +76,20 @@ static function (PhpDocTagValueNode $value): bool {
}


/**
* @return TypelessParamTagValueNode[]
*/
public function getTypelessParamTagValues(string $tagName = '@param'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof TypelessParamTagValueNode;
}
);
}


/**
* @return TemplateTagValueNode[]
*/
Expand Down
41 changes: 41 additions & 0 deletions src/Ast/PhpDoc/TypelessParamTagValueNode.php
@@ -0,0 +1,41 @@
<?php declare(strict_types = 1);

namespace PHPStan\PhpDocParser\Ast\PhpDoc;

use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function trim;

class TypelessParamTagValueNode implements PhpDocTagValueNode
{

use NodeAttributes;

/** @var bool */
public $isReference;

/** @var bool */
public $isVariadic;

/** @var string */
public $parameterName;

/** @var string (may be empty) */
public $description;

public function __construct(bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
{
$this->isReference = $isReference;
$this->isVariadic = $isVariadic;
$this->parameterName = $parameterName;
$this->description = $description;
}


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

}
24 changes: 20 additions & 4 deletions src/Parser/PhpDocParser.php
Expand Up @@ -230,14 +230,31 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
}


private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamTagValueNode
/**
* @return Ast\PhpDoc\ParamTagValueNode|Ast\PhpDoc\TypelessParamTagValueNode
*/
private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode
{
$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);
return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description, $isReference);

if ($type !== null) {
return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description, $isReference);
}

return new Ast\PhpDoc\TypelessParamTagValueNode($isVariadic, $parameterName, $description, $isReference);
}


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


private function parseOptionalDescription(TokenIterator $tokens, bool $limitStartToken = false): string
{
if ($limitStartToken) {
Expand Down
82 changes: 82 additions & 0 deletions tests/PHPStan/Parser/PhpDocParserTest.php
Expand Up @@ -27,6 +27,7 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TypelessParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\UsesTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
Expand Down Expand Up @@ -64,6 +65,7 @@ protected function setUp(): void
/**
* @dataProvider provideTagsWithNumbers
* @dataProvider provideParamTagsData
* @dataProvider provideTypelessParamTagsData
* @dataProvider provideVarTagsData
* @dataProvider provideReturnTagsData
* @dataProvider provideThrowsTagsData
Expand Down Expand Up @@ -395,6 +397,86 @@ public function provideParamTagsData(): Iterator
];
}

public function provideTypelessParamTagsData(): Iterator
{
yield [
'OK',
'/** @param $foo description */',
new PhpDocNode([
new PhpDocTagNode(
'@param',
new TypelessParamTagValueNode(
false,
'$foo',
'description'
)
),
]),
];

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

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

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

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

public function provideVarTagsData(): Iterator
{
Expand Down