Make requiring whitespace before description optional

This commit is contained in:
Richard van Velzen 2022-06-09 08:32:10 +02:00 committed by Ondřej Mirtes
parent 1e355a35e2
commit 3cb62d1084
2 changed files with 91 additions and 9 deletions

View File

@ -24,10 +24,14 @@ class PhpDocParser
/** @var ConstExprParser */ /** @var ConstExprParser */
private $constantExprParser; private $constantExprParser;
public function __construct(TypeParser $typeParser, ConstExprParser $constantExprParser) /** @var bool */
private $requireWhitespaceBeforeDescription;
public function __construct(TypeParser $typeParser, ConstExprParser $constantExprParser, bool $requireWhitespaceBeforeDescription = false)
{ {
$this->typeParser = $typeParser; $this->typeParser = $typeParser;
$this->constantExprParser = $constantExprParser; $this->constantExprParser = $constantExprParser;
$this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription;
} }
@ -492,7 +496,8 @@ class PhpDocParser
} }
if ( if (
!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END) $this->requireWhitespaceBeforeDescription
&& !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END)
&& !$tokens->isPrecededByHorizontalWhitespace() && !$tokens->isPrecededByHorizontalWhitespace()
) { ) {
$tokens->consumeTokenType(Lexer::TOKEN_HORIZONTAL_WS); // will throw exception $tokens->consumeTokenType(Lexer::TOKEN_HORIZONTAL_WS); // will throw exception

View File

@ -53,12 +53,17 @@ class PhpDocParserTest extends TestCase
/** @var PhpDocParser */ /** @var PhpDocParser */
private $phpDocParser; private $phpDocParser;
/** @var PhpDocParser */
private $phpDocParserWithRequiredWhitespaceBeforeDescription;
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
$this->lexer = new Lexer(); $this->lexer = new Lexer();
$constExprParser = new ConstExprParser(); $constExprParser = new ConstExprParser();
$this->phpDocParser = new PhpDocParser(new TypeParser($constExprParser), $constExprParser); $typeParser = new TypeParser($constExprParser);
$this->phpDocParser = new PhpDocParser($typeParser, $constExprParser);
$this->phpDocParserWithRequiredWhitespaceBeforeDescription = new PhpDocParser($typeParser, $constExprParser, true);
} }
@ -83,14 +88,37 @@ class PhpDocParserTest extends TestCase
* @dataProvider provideRealWorldExampleData * @dataProvider provideRealWorldExampleData
* @dataProvider provideDescriptionWithOrWithoutHtml * @dataProvider provideDescriptionWithOrWithoutHtml
*/ */
public function testParse(string $label, string $input, PhpDocNode $expectedPhpDocNode, int $nextTokenType = Lexer::TOKEN_END): void public function testParse(
string $label,
string $input,
PhpDocNode $expectedPhpDocNode,
?PhpDocNode $withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode = null
): void
{
$this->executeTestParse(
$this->phpDocParser,
$label,
$input,
$expectedPhpDocNode
);
$this->executeTestParse(
$this->phpDocParserWithRequiredWhitespaceBeforeDescription,
$label,
$input,
$withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode ?? $expectedPhpDocNode
);
}
private function executeTestParse(PhpDocParser $phpDocParser, string $label, string $input, PhpDocNode $expectedPhpDocNode): void
{ {
$tokens = new TokenIterator($this->lexer->tokenize($input)); $tokens = new TokenIterator($this->lexer->tokenize($input));
$actualPhpDocNode = $this->phpDocParser->parse($tokens); $actualPhpDocNode = $phpDocParser->parse($tokens);
$this->assertEquals($expectedPhpDocNode, $actualPhpDocNode, $label); $this->assertEquals($expectedPhpDocNode, $actualPhpDocNode, $label);
$this->assertSame((string) $expectedPhpDocNode, (string) $actualPhpDocNode); $this->assertSame((string) $expectedPhpDocNode, (string) $actualPhpDocNode);
$this->assertSame($nextTokenType, $tokens->currentTokenType()); $this->assertSame(Lexer::TOKEN_END, $tokens->currentTokenType());
} }
@ -675,6 +703,16 @@ class PhpDocParserTest extends TestCase
) )
), ),
]), ]),
new PhpDocNode([
new PhpDocTagNode(
'@var',
new VarTagValueNode(
new IdentifierTypeNode('Foo'),
'$foo',
'#desc'
)
),
]),
]; ];
yield [ yield [
@ -1459,6 +1497,15 @@ class PhpDocParserTest extends TestCase
yield [ yield [
'invalid variadic callable', 'invalid variadic callable',
'/** @return \Closure(...int, string): string */', '/** @return \Closure(...int, string): string */',
new PhpDocNode([
new PhpDocTagNode(
'@return',
new ReturnTagValueNode(
new IdentifierTypeNode('\Closure'),
'(...int, string): string'
)
),
]),
new PhpDocNode([ new PhpDocNode([
new PhpDocTagNode( new PhpDocTagNode(
'@return', '@return',
@ -2265,6 +2312,16 @@ class PhpDocParserTest extends TestCase
yield [ yield [
'callable with incomplete signature without return type', 'callable with incomplete signature without return type',
'/** @var callable(int) */', '/** @var callable(int) */',
new PhpDocNode([
new PhpDocTagNode(
'@var',
new VarTagValueNode(
new IdentifierTypeNode('callable'),
'',
'(int)'
)
),
]),
new PhpDocNode([ new PhpDocNode([
new PhpDocTagNode( new PhpDocTagNode(
'@var', '@var',
@ -4241,6 +4298,20 @@ Finder::findFiles('*.php')
'/**' . PHP_EOL . '/**' . PHP_EOL .
' * @return Foo <strong>Important description' . PHP_EOL . ' * @return Foo <strong>Important description' . PHP_EOL .
' */', ' */',
new PhpDocNode([
new PhpDocTagNode(
'@return',
new ReturnTagValueNode(
new GenericTypeNode(
new IdentifierTypeNode('Foo'),
[
new IdentifierTypeNode('strong'),
]
),
'Important description'
)
),
]),
new PhpDocNode([ new PhpDocNode([
new PhpDocTagNode( new PhpDocTagNode(
'@return', '@return',
@ -4305,14 +4376,20 @@ Finder::findFiles('*.php')
* @dataProvider dataParseTagValue * @dataProvider dataParseTagValue
* @param PhpDocNode $expectedPhpDocNode * @param PhpDocNode $expectedPhpDocNode
*/ */
public function testParseTagValue(string $tag, string $phpDoc, Node $expectedPhpDocNode, int $nextTokenType = Lexer::TOKEN_END): void public function testParseTagValue(string $tag, string $phpDoc, Node $expectedPhpDocNode): void
{
$this->executeTestParseTagValue($this->phpDocParser, $tag, $phpDoc, $expectedPhpDocNode);
$this->executeTestParseTagValue($this->phpDocParserWithRequiredWhitespaceBeforeDescription, $tag, $phpDoc, $expectedPhpDocNode);
}
private function executeTestParseTagValue(PhpDocParser $phpDocParser, string $tag, string $phpDoc, Node $expectedPhpDocNode): void
{ {
$tokens = new TokenIterator($this->lexer->tokenize($phpDoc)); $tokens = new TokenIterator($this->lexer->tokenize($phpDoc));
$actualPhpDocNode = $this->phpDocParser->parseTagValue($tokens, $tag); $actualPhpDocNode = $phpDocParser->parseTagValue($tokens, $tag);
$this->assertEquals($expectedPhpDocNode, $actualPhpDocNode); $this->assertEquals($expectedPhpDocNode, $actualPhpDocNode);
$this->assertSame((string) $expectedPhpDocNode, (string) $actualPhpDocNode); $this->assertSame((string) $expectedPhpDocNode, (string) $actualPhpDocNode);
$this->assertSame($nextTokenType, $tokens->currentTokenType()); $this->assertSame(Lexer::TOKEN_END, $tokens->currentTokenType());
} }
} }