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 28cb38b

Browse files
rvanvelzenondrejmirtes
authored andcommitted
Implement offset access type syntax
1 parent 6cafed9 commit 28cb38b

File tree

4 files changed

+76
-11
lines changed

4 files changed

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

‎src/Parser/TypeParser.php‎

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
8080
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
8181

8282
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
83-
return $this->tryParseArray($tokens, $type);
83+
return $this->tryParseArrayOrOffsetAccess($tokens, $type);
8484
}
8585

8686
return $type;
@@ -90,7 +90,7 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
9090
$type = new Ast\Type\ThisTypeNode();
9191

9292
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
93-
return $this->tryParseArray($tokens, $type);
93+
return $this->tryParseArrayOrOffsetAccess($tokens, $type);
9494
}
9595

9696
return $type;
@@ -115,19 +115,19 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
115115
$type = $this->parseGeneric($tokens, $type);
116116

117117
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
118-
$type = $this->tryParseArray($tokens, $type);
118+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
119119
}
120120
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
121121
$type = $this->tryParseCallable($tokens, $type);
122122

123123
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
124-
$type = $this->tryParseArray($tokens, $type);
124+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
125125

126126
} elseif ($type->name === 'array' && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
127127
$type = $this->parseArrayShape($tokens, $type);
128128

129129
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
130-
$type = $this->tryParseArray($tokens, $type);
130+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
131131
}
132132
}
133133

@@ -293,7 +293,7 @@ private function parseNullable(TokenIterator $tokens): Ast\Type\TypeNode
293293
}
294294

295295
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
296-
$type = $this->tryParseArray($tokens, $type);
296+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
297297
}
298298

299299
return new Ast\Type\NullableTypeNode($type);
@@ -427,7 +427,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
427427
}
428428

429429
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
430-
$type = $this->tryParseArray($tokens, $type);
430+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
431431
}
432432

433433
return $type;
@@ -452,15 +452,25 @@ private function tryParseCallable(TokenIterator $tokens, Ast\Type\IdentifierType
452452

453453

454454
/** @phpstan-impure */
455-
private function tryParseArray(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
455+
private function tryParseArrayOrOffsetAccess(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
456456
{
457457
try {
458458
while ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
459459
$tokens->pushSavePoint();
460+
461+
$canBeOffsetAccessType = !$tokens->isPrecededByHorizontalWhitespace();
460462
$tokens->consumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET);
461-
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
462-
$tokens->dropSavePoint();
463-
$type = new Ast\Type\ArrayTypeNode($type);
463+
464+
if ($canBeOffsetAccessType && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET)) {
465+
$offset = $this->parse($tokens);
466+
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
467+
$tokens->dropSavePoint();
468+
$type = new Ast\Type\OffsetAccessTypeNode($type, $offset);
469+
} else {
470+
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
471+
$tokens->dropSavePoint();
472+
$type = new Ast\Type\ArrayTypeNode($type);
473+
}
464474
}
465475

466476
} catch (ParserException $e) {

‎tests/PHPStan/Parser/PhpDocParserTest.php‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
3838
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
3939
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
40+
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
4041
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
4142
use PHPStan\PhpDocParser\Lexer\Lexer;
4243
use PHPUnit\Framework\TestCase;
@@ -974,6 +975,23 @@ public function provideReturnTagsData(): Iterator
974975
]),
975976
];
976977

978+
yield [
979+
'OK with offset access type',
980+
'/** @return Foo[Bar] */',
981+
new PhpDocNode([
982+
new PhpDocTagNode(
983+
'@return',
984+
new ReturnTagValueNode(
985+
new OffsetAccessTypeNode(
986+
new IdentifierTypeNode('Foo'),
987+
new IdentifierTypeNode('Bar')
988+
),
989+
''
990+
)
991+
),
992+
]),
993+
];
994+
977995
yield [
978996
'invalid without type and description',
979997
'/** @return */',

‎tests/PHPStan/Parser/TypeParserTest.php‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
2020
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
2121
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
22+
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
2223
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
2324
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
2425
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
@@ -747,6 +748,13 @@ public function provideParseData(): array
747748
new IdentifierTypeNode('array'),
748749
Lexer::TOKEN_OPEN_SQUARE_BRACKET,
749750
],
751+
[
752+
'array[ int ]',
753+
new OffsetAccessTypeNode(
754+
new IdentifierTypeNode('array'),
755+
new IdentifierTypeNode('int')
756+
),
757+
],
750758
[
751759
"?\t\xA009", // edge-case with \h
752760
new NullableTypeNode(

0 commit comments

Comments
(0)

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