Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 32417d9

Browse files
jiripudilondrejmirtes
authored andcommitted
parse type projections
1 parent 22a7b24 commit 32417d9

File tree

5 files changed

+134
-3
lines changed

5 files changed

+134
-3
lines changed

‎doc/grammars/type.abnf

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ Atomic
2828
/ TokenParenthesesOpen ParenthesizedType TokenParenthesesClose [Array]
2929

3030
Generic
31-
= TokenAngleBracketOpen Type *(TokenComma Type) TokenAngleBracketClose
31+
= TokenAngleBracketOpen GenericTypeArgument *(TokenComma GenericTypeArgument) TokenAngleBracketClose
32+
33+
GenericTypeArgument
34+
= [TokenContravariant / TokenCovariant] Type
35+
/ TokenWildcard
3236

3337
Callable
3438
= TokenParenthesesOpen [CallableParameters] TokenParenthesesClose TokenColon CallableReturnType
@@ -188,6 +192,15 @@ TokenIs
188192
TokenNot
189193
= %s"not" 1*ByteHorizontalWs
190194

195+
TokenContravariant
196+
= %x63.6F.6E.74.72.61.76.61.72.69.61.6E.74 1*ByteHorizontalWs
197+
198+
TokenCovariant
199+
= %x63.6F.76.61.72.69.61.6E.74 1*ByteHorizontalWs
200+
201+
TokenWildcard
202+
= "*" *ByteHorizontalWs
203+
191204
TokenIdentifier
192205
= [ByteBackslash] ByteIdentifierFirst *ByteIdentifierSecond *(ByteBackslash ByteIdentifierFirst *ByteIdentifierSecond) *ByteHorizontalWs
193206

‎src/Ast/Type/StarProjectionNode.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast\Type;
4+
5+
use PHPStan\PhpDocParser\Ast\NodeAttributes;
6+
7+
final class StarProjectionNode implements TypeNode
8+
{
9+
10+
use NodeAttributes;
11+
12+
public function __toString(): string
13+
{
14+
return '*';
15+
}
16+
17+
}

‎src/Ast/Type/TypeProjectionNode.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast\Type;
4+
5+
use PHPStan\PhpDocParser\Ast\NodeAttributes;
6+
7+
class TypeProjectionNode implements TypeNode
8+
{
9+
10+
use NodeAttributes;
11+
12+
/** @var TypeNode */
13+
public $type;
14+
15+
/** @var 'covariant'|'contravariant' */
16+
public $variance;
17+
18+
/**
19+
* @param 'covariant'|'contravariant' $variance
20+
*/
21+
public function __construct(TypeNode $type, string $variance)
22+
{
23+
$this->type = $type;
24+
$this->variance = $variance;
25+
}
26+
27+
28+
public function __toString(): string
29+
{
30+
return $this->variance . '' . $this->type;
31+
}
32+
33+
}

‎src/Parser/TypeParser.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode
323323
{
324324
$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
325325
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
326-
$genericTypes = [$this->parse($tokens)];
326+
$genericTypes = [$this->parseGenericTypeArgument($tokens)];
327327

328328
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
329329

@@ -333,7 +333,7 @@ public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode
333333
// trailing comma case
334334
return new Ast\Type\GenericTypeNode($baseType, $genericTypes);
335335
}
336-
$genericTypes[] = $this->parse($tokens);
336+
$genericTypes[] = $this->parseGenericTypeArgument($tokens);
337337
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
338338
}
339339

@@ -344,6 +344,27 @@ public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode
344344
}
345345

346346

347+
/** @phpstan-impure */
348+
public function parseGenericTypeArgument(TokenIterator $tokens): Ast\Type\TypeNode
349+
{
350+
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) {
351+
return new Ast\Type\StarProjectionNode();
352+
}
353+
354+
if ($tokens->tryConsumeTokenValue('contravariant')) {
355+
$variance = 'contravariant';
356+
} elseif ($tokens->tryConsumeTokenValue('covariant')) {
357+
$variance = 'covariant';
358+
} else {
359+
return $this->parse($tokens);
360+
}
361+
362+
$type = $this->parse($tokens);
363+
364+
return new Ast\Type\TypeProjectionNode($type, $variance);
365+
}
366+
367+
347368
/** @phpstan-impure */
348369
private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier): Ast\Type\TypeNode
349370
{

‎tests/PHPStan/Parser/TypeParserTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
2121
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
2222
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
23+
use PHPStan\PhpDocParser\Ast\Type\StarProjectionNode;
2324
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
2425
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
26+
use PHPStan\PhpDocParser\Ast\Type\TypeProjectionNode;
2527
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
2628
use PHPStan\PhpDocParser\Lexer\Lexer;
2729
use PHPUnit\Framework\TestCase;
@@ -1266,6 +1268,51 @@ public function provideParseData(): array
12661268
)
12671269
),
12681270
],
1271+
[
1272+
'Foo<covariant Bar, Baz>',
1273+
new GenericTypeNode(
1274+
new IdentifierTypeNode('Foo'),
1275+
[
1276+
new TypeProjectionNode(
1277+
new IdentifierTypeNode('Bar'),
1278+
'covariant'
1279+
),
1280+
new IdentifierTypeNode('Baz'),
1281+
]
1282+
),
1283+
],
1284+
[
1285+
'Foo<Bar, contravariant Baz>',
1286+
new GenericTypeNode(
1287+
new IdentifierTypeNode('Foo'),
1288+
[
1289+
new IdentifierTypeNode('Bar'),
1290+
new TypeProjectionNode(
1291+
new IdentifierTypeNode('Baz'),
1292+
'contravariant'
1293+
),
1294+
]
1295+
),
1296+
],
1297+
[
1298+
'Foo<covariant>',
1299+
new ParserException(
1300+
'>',
1301+
Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
1302+
13,
1303+
Lexer::TOKEN_IDENTIFIER
1304+
),
1305+
],
1306+
[
1307+
'Foo<Bar, *>',
1308+
new GenericTypeNode(
1309+
new IdentifierTypeNode('Foo'),
1310+
[
1311+
new IdentifierTypeNode('Bar'),
1312+
new StarProjectionNode(),
1313+
]
1314+
),
1315+
],
12691316
];
12701317
}
12711318

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /