diff --git a/doc/grammars/phpdoc-param.peg b/doc/grammars/phpdoc-param.peg index 5a5c1b3..16c206e 100644 --- a/doc/grammars/phpdoc-param.peg +++ b/doc/grammars/phpdoc-param.peg @@ -1,9 +1,12 @@ PhpDocParam - = AnnotationName Type IsVariadic? ParameterName Description? + = AnnotationName Type IsReference? IsVariadic? ParameterName Description? AnnotationName = '@param' +IsReference + = '&' + IsVariaric = '...' diff --git a/src/Ast/PhpDoc/ParamTagValueNode.php b/src/Ast/PhpDoc/ParamTagValueNode.php index eb88cd9..874180e 100644 --- a/src/Ast/PhpDoc/ParamTagValueNode.php +++ b/src/Ast/PhpDoc/ParamTagValueNode.php @@ -13,6 +13,9 @@ class ParamTagValueNode implements PhpDocTagValueNode /** @var TypeNode */ public $type; + /** @var bool */ + public $isReference; + /** @var bool */ public $isVariadic; @@ -22,9 +25,10 @@ class ParamTagValueNode implements PhpDocTagValueNode /** @var string (may be empty) */ public $description; - public function __construct(TypeNode $type, bool $isVariadic, string $parameterName, string $description) + public function __construct(TypeNode $type, bool $isReference, bool $isVariadic, string $parameterName, string $description) { $this->type = $type; + $this->isReference = $isReference; $this->isVariadic = $isVariadic; $this->parameterName = $parameterName; $this->description = $description; @@ -33,8 +37,9 @@ class ParamTagValueNode implements PhpDocTagValueNode public function __toString(): string { + $reference = $this->isReference ? '&' : ''; $variadic = $this->isVariadic ? '...' : ''; - return trim("{$this->type} {$variadic}{$this->parameterName} {$this->description}"); + return trim("{$this->type} {$reference}{$variadic}{$this->parameterName} {$this->description}"); } } diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php index 704c69a..4231bcb 100644 --- a/src/Parser/PhpDocParser.php +++ b/src/Parser/PhpDocParser.php @@ -220,10 +220,11 @@ class PhpDocParser private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamTagValueNode { $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); + return new Ast\PhpDoc\ParamTagValueNode($type, $isReference, $isVariadic, $parameterName, $description); } diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index c65b032..a632e0e 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -99,6 +99,7 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, + false, '$foo', '' ) @@ -115,6 +116,7 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, + false, '$foo', 'optional description' ) @@ -130,6 +132,7 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase '@param', new ParamTagValueNode( new IdentifierTypeNode('Foo'), + false, true, '$foo', '' @@ -146,6 +149,75 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase '@param', new ParamTagValueNode( new IdentifierTypeNode('Foo'), + false, + true, + '$foo', + 'optional description' + ) + ), + ]), + ]; + + yield [ + 'OK reference without description', + '/** @param Foo &$foo */', + new PhpDocNode([ + new PhpDocTagNode( + '@param', + new ParamTagValueNode( + new IdentifierTypeNode('Foo'), + true, + false, + '$foo', + '' + ) + ), + ]), + ]; + + yield [ + 'OK reference with description', + '/** @param Foo &$foo optional description */', + new PhpDocNode([ + new PhpDocTagNode( + '@param', + new ParamTagValueNode( + new IdentifierTypeNode('Foo'), + true, + false, + '$foo', + 'optional description' + ) + ), + ]), + ]; + + yield [ + 'OK reference variadic without description', + '/** @param Foo &...$foo */', + new PhpDocNode([ + new PhpDocTagNode( + '@param', + new ParamTagValueNode( + new IdentifierTypeNode('Foo'), + true, + true, + '$foo', + '' + ) + ), + ]), + ]; + + yield [ + 'OK reference variadic with description', + '/** @param Foo &...$foo optional description */', + new PhpDocNode([ + new PhpDocTagNode( + '@param', + new ParamTagValueNode( + new IdentifierTypeNode('Foo'), + true, true, '$foo', 'optional description' @@ -1827,6 +1899,7 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, + false, '$foo', '1st multi world description' ) @@ -1836,6 +1909,7 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase new ParamTagValueNode( new IdentifierTypeNode('Bar'), false, + false, '$bar', '2nd multi world description' ) @@ -1855,6 +1929,7 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, + false, '$foo', '1st multi world description some text in the middle' @@ -1865,6 +1940,7 @@ some text in the middle' new ParamTagValueNode( new IdentifierTypeNode('Bar'), false, + false, '$bar', '2nd multi world description' ) @@ -1895,6 +1971,7 @@ some text in the middle' new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, + false, '$foo', '1st multi world description with empty lines' ) @@ -1909,6 +1986,7 @@ some text in the middle' new ParamTagValueNode( new IdentifierTypeNode('Bar'), false, + false, '$bar', '2nd multi world description with empty lines' ) @@ -1942,6 +2020,7 @@ some text in the middle' new ParamTagValueNode( new IdentifierTypeNode('int'), false, + false, '$foo', '@param string $bar' ) @@ -2857,6 +2936,7 @@ some text in the middle' new ParamTagValueNode( new IdentifierTypeNode('class-string'), false, + false, '$test', '' ) @@ -2873,6 +2953,7 @@ some text in the middle' new ParamTagValueNode( new IdentifierTypeNode('class-string'), false, + false, '$test', 'some description' ) @@ -3166,6 +3247,7 @@ time are not reliable as field settings might be missing.'), new ParamTagValueNode( new IdentifierTypeNode('\Drupal\Core\Field\FieldStorageDefinitionInterface'), false, + false, '$field_definition', '' ) @@ -3243,6 +3325,7 @@ time are not reliable as field settings might be missing.'), new ParamTagValueNode( new IdentifierTypeNode('Request'), false, + false, '$request', '- The request object' ) @@ -3463,6 +3546,7 @@ Finder::findFiles('*.php') new ParamTagValueNode( new ConstTypeNode(new ConstFetchNode('DateTimeImmutable', 'ATOM')), false, + false, '$a', '' ),