Skip to content

Commit

Permalink
Merge branch '1.20.x' into 1.21.x
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed May 2, 2023
2 parents 97d18ea + 7d568c8 commit cc94635
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 27 deletions.
133 changes: 106 additions & 27 deletions src/Parser/TypeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -522,47 +522,126 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
$type = $this->parseNullable($tokens);
return $this->parseNullable($tokens);

} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
$type = $this->parse($tokens);
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
}

} else {
$type = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue());
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);

if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
$type = $this->parseGeneric(
$tokens,
$this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
)
);

} elseif (in_array($type->name, ['array', 'list'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
$type = $this->parseArrayShape($tokens, $this->enrichWithAttributes(
return $type;
} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
$type = new Ast\Type\ThisTypeNode();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
), $type->name);
));
}

return $type;
} else {
$currentTokenValue = $tokens->currentTokenValue();
$tokens->pushSavePoint(); // because of ConstFetchNode
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
$type = new Ast\Type\IdentifierTypeNode($currentTokenValue);

if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
$type = $this->parseGeneric(
$tokens,
$this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
)
);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));
}

} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));

} elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
if ($type->name === 'object') {
$type = $this->parseObjectShape($tokens);
} else {
$type = $this->parseArrayShape($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
), $type->name);
}

if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));
}
}

return $type;
} else {
$tokens->rollback(); // because of ConstFetchNode
}
} else {
$tokens->dropSavePoint(); // because of ConstFetchNode
}
}

if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));
$exception = new ParserException(
$tokens->currentTokenValue(),
$tokens->currentTokenType(),
$tokens->currentTokenOffset(),
Lexer::TOKEN_IDENTIFIER,
null,
$tokens->currentTokenLine()
);

if ($this->constExprParser === null) {
throw $exception;
}

return $type;
try {
$constExpr = $this->constExprParser->parse($tokens, true);
if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
throw $exception;
}

$type = new Ast\Type\ConstTypeNode($constExpr);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
$tokens,
$type,
$startLine,
$startIndex
));
}

return $type;
} catch (LogicException $e) {
throw $exception;
}
}


Expand Down
87 changes: 87 additions & 0 deletions tests/PHPStan/Parser/TypeParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,22 @@ public function provideParseData(): array
)
),
],
[
'callable(): Foo<Bar>[]',
new CallableTypeNode(
new IdentifierTypeNode('callable'),
[],
new ArrayTypeNode(new GenericTypeNode(
new IdentifierTypeNode('Foo'),
[
new IdentifierTypeNode('Bar'),
],
[
GenericTypeNode::VARIANCE_INVARIANT,
]
))
),
],
[
'callable(): Foo|Bar',
new UnionTypeNode([
Expand Down Expand Up @@ -1956,6 +1972,77 @@ public function provideParseData(): array
'callable(): ?int',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new NullableTypeNode(new IdentifierTypeNode('int'))),
],
[
'callable(): object{foo: int}',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ObjectShapeNode([
new ObjectShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
])),
],
[
'callable(): object{foo: int}[]',
new CallableTypeNode(
new IdentifierTypeNode('callable'),
[],
new ArrayTypeNode(
new ObjectShapeNode([
new ObjectShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
])
)
),
],
[
'callable(): $this',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ThisTypeNode()),
],
[
'callable(): $this[]',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ThisTypeNode())),
],
[
'2.5|3',
new UnionTypeNode([
new ConstTypeNode(new ConstExprFloatNode('2.5')),
new ConstTypeNode(new ConstExprIntegerNode('3')),
]),
],
[
'callable(): 3.5',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstExprFloatNode('3.5'))),
],
[
'callable(): 3.5[]',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(
new ConstTypeNode(new ConstExprFloatNode('3.5'))
)),
],
[
'callable(): Foo',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new IdentifierTypeNode('Foo')),
],
[
'callable(): (Foo)[]',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('Foo'))),
],
[
'callable(): Foo::BAR',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstFetchNode('Foo', 'BAR'))),
],
[
'callable(): Foo::*',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstFetchNode('Foo', '*'))),
],
[
'?Foo[]',
new NullableTypeNode(new ArrayTypeNode(new IdentifierTypeNode('Foo'))),
],
[
'callable(): ?Foo',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new NullableTypeNode(new IdentifierTypeNode('Foo'))),
],
[
'callable(): ?Foo[]',
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new NullableTypeNode(new ArrayTypeNode(new IdentifierTypeNode('Foo')))),
],
];
}

Expand Down

0 comments on commit cc94635

Please sign in to comment.