Skip to content

Commit bfec872

Browse files
committed
PhpDocParser - option to preserve type alias with parse error
1 parent afb728a commit bfec872

File tree

3 files changed

+171
-2
lines changed

3 files changed

+171
-2
lines changed

Diff for: src/Ast/Type/InvalidTypeNode.php

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast\Type;
4+
5+
use PHPStan\PhpDocParser\Ast\NodeAttributes;
6+
use PHPStan\PhpDocParser\Parser\ParserException;
7+
8+
class InvalidTypeNode implements TypeNode
9+
{
10+
11+
use NodeAttributes;
12+
13+
/** @var mixed[] */
14+
private $exceptionArgs;
15+
16+
public function __construct(ParserException $exception)
17+
{
18+
$this->exceptionArgs = [
19+
$exception->getCurrentTokenValue(),
20+
$exception->getCurrentTokenType(),
21+
$exception->getCurrentOffset(),
22+
$exception->getExpectedTokenType(),
23+
$exception->getExpectedTokenValue(),
24+
];
25+
}
26+
27+
public function getException(): ParserException
28+
{
29+
return new ParserException(...$this->exceptionArgs);
30+
}
31+
32+
public function __toString(): string
33+
{
34+
return '*Invalid type*';
35+
}
36+
37+
}

Diff for: src/Parser/PhpDocParser.php

+15-1
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ class PhpDocParser
2828
/** @var bool */
2929
private $requireWhitespaceBeforeDescription;
3030

31-
public function __construct(TypeParser $typeParser, ConstExprParser $constantExprParser, bool $requireWhitespaceBeforeDescription = false)
31+
/** @var bool */
32+
private $preserveTypeAliasesWithInvalidTypes;
33+
34+
public function __construct(TypeParser $typeParser, ConstExprParser $constantExprParser, bool $requireWhitespaceBeforeDescription = false, bool $preserveTypeAliasesWithInvalidTypes = false)
3235
{
3336
$this->typeParser = $typeParser;
3437
$this->constantExprParser = $constantExprParser;
3538
$this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription;
39+
$this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes;
3640
}
3741

3842

@@ -453,6 +457,16 @@ private function parseTypeAliasTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeA
453457
// support psalm-type syntax
454458
$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
455459

460+
if ($this->preserveTypeAliasesWithInvalidTypes) {
461+
try {
462+
$type = $this->typeParser->parse($tokens);
463+
464+
return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
465+
} catch (ParserException $e) {
466+
return new Ast\PhpDoc\TypeAliasTagValueNode($alias, new Ast\Type\InvalidTypeNode($e));
467+
}
468+
}
469+
456470
$type = $this->typeParser->parse($tokens);
457471

458472
return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);

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

+119-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
4646
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
4747
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
48+
use PHPStan\PhpDocParser\Ast\Type\InvalidTypeNode;
4849
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
4950
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
5051
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
@@ -64,6 +65,9 @@ class PhpDocParserTest extends TestCase
6465
/** @var PhpDocParser */
6566
private $phpDocParserWithRequiredWhitespaceBeforeDescription;
6667

68+
/** @var PhpDocParser */
69+
private $phpDocParserWithPreserveTypeAliasesWithInvalidTypes;
70+
6771
protected function setUp(): void
6872
{
6973
parent::setUp();
@@ -72,6 +76,7 @@ protected function setUp(): void
7276
$typeParser = new TypeParser($constExprParser);
7377
$this->phpDocParser = new PhpDocParser($typeParser, $constExprParser);
7478
$this->phpDocParserWithRequiredWhitespaceBeforeDescription = new PhpDocParser($typeParser, $constExprParser, true);
79+
$this->phpDocParserWithPreserveTypeAliasesWithInvalidTypes = new PhpDocParser($typeParser, $constExprParser, true, true);
7580
}
7681

7782

@@ -104,7 +109,8 @@ public function testParse(
104109
string $label,
105110
string $input,
106111
PhpDocNode $expectedPhpDocNode,
107-
?PhpDocNode $withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode = null
112+
?PhpDocNode $withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode = null,
113+
?PhpDocNode $withPreserveTypeAliasesWithInvalidTypesExpectedPhpDocNode = null
108114
): void
109115
{
110116
$this->executeTestParse(
@@ -120,6 +126,13 @@ public function testParse(
120126
$input,
121127
$withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode ?? $expectedPhpDocNode
122128
);
129+
130+
$this->executeTestParse(
131+
$this->phpDocParserWithPreserveTypeAliasesWithInvalidTypes,
132+
$label,
133+
$input,
134+
$withPreserveTypeAliasesWithInvalidTypesExpectedPhpDocNode ?? $withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode ?? $expectedPhpDocNode
135+
);
123136
}
124137

125138

@@ -3834,6 +3847,111 @@ public function provideTypeAliasTagsData(): Iterator
38343847
)
38353848
),
38363849
]),
3850+
null,
3851+
new PhpDocNode([
3852+
new PhpDocTagNode(
3853+
'@phpstan-type',
3854+
new TypeAliasTagValueNode(
3855+
'TypeAlias',
3856+
new InvalidTypeNode(new ParserException(
3857+
'*/',
3858+
Lexer::TOKEN_CLOSE_PHPDOC,
3859+
28,
3860+
Lexer::TOKEN_IDENTIFIER,
3861+
null
3862+
))
3863+
)
3864+
),
3865+
]),
3866+
];
3867+
3868+
yield [
3869+
'invalid without type with newline',
3870+
'/**
3871+
* @phpstan-type TypeAlias
3872+
*/',
3873+
new PhpDocNode([
3874+
new PhpDocTagNode(
3875+
'@phpstan-type',
3876+
new InvalidTagValueNode(
3877+
'TypeAlias',
3878+
new ParserException(
3879+
"\n\t\t\t ",
3880+
Lexer::TOKEN_PHPDOC_EOL,
3881+
34,
3882+
Lexer::TOKEN_IDENTIFIER
3883+
)
3884+
)
3885+
),
3886+
]),
3887+
null,
3888+
new PhpDocNode([
3889+
new PhpDocTagNode(
3890+
'@phpstan-type',
3891+
new TypeAliasTagValueNode(
3892+
'TypeAlias',
3893+
new InvalidTypeNode(new ParserException(
3894+
"\n\t\t\t ",
3895+
Lexer::TOKEN_PHPDOC_EOL,
3896+
34,
3897+
Lexer::TOKEN_IDENTIFIER,
3898+
null
3899+
))
3900+
)
3901+
),
3902+
]),
3903+
];
3904+
3905+
yield [
3906+
'invalid without type but valid tag below',
3907+
'/**
3908+
* @phpstan-type TypeAlias
3909+
* @mixin T
3910+
*/',
3911+
new PhpDocNode([
3912+
new PhpDocTagNode(
3913+
'@phpstan-type',
3914+
new InvalidTagValueNode(
3915+
'TypeAlias',
3916+
new ParserException(
3917+
"\n\t\t\t * ",
3918+
Lexer::TOKEN_PHPDOC_EOL,
3919+
34,
3920+
Lexer::TOKEN_IDENTIFIER
3921+
)
3922+
)
3923+
),
3924+
new PhpDocTagNode(
3925+
'@mixin',
3926+
new MixinTagValueNode(
3927+
new IdentifierTypeNode('T'),
3928+
''
3929+
)
3930+
),
3931+
]),
3932+
null,
3933+
new PhpDocNode([
3934+
new PhpDocTagNode(
3935+
'@phpstan-type',
3936+
new TypeAliasTagValueNode(
3937+
'TypeAlias',
3938+
new InvalidTypeNode(new ParserException(
3939+
"\n\t\t\t * ",
3940+
Lexer::TOKEN_PHPDOC_EOL,
3941+
34,
3942+
Lexer::TOKEN_IDENTIFIER,
3943+
null
3944+
))
3945+
)
3946+
),
3947+
new PhpDocTagNode(
3948+
'@mixin',
3949+
new MixinTagValueNode(
3950+
new IdentifierTypeNode('T'),
3951+
''
3952+
)
3953+
),
3954+
]),
38373955
];
38383956

38393957
yield [

0 commit comments

Comments
 (0)