Skip to content

Commit a7e9698

Browse files
committed
CallableTypeNode - support ConstTypeNode in return type
1 parent 421d3f3 commit a7e9698

File tree

2 files changed

+163
-30
lines changed

2 files changed

+163
-30
lines changed

Diff for: src/Parser/TypeParser.php

+114-30
Original file line numberDiff line numberDiff line change
@@ -522,51 +522,135 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
522522
$startIndex = $tokens->currentTokenIndex();
523523
if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
524524
$type = $this->parseNullable($tokens);
525+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
526+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
527+
$tokens,
528+
$type,
529+
$startLine,
530+
$startIndex
531+
));
532+
}
533+
534+
return $type;
525535

526536
} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
527537
$type = $this->parse($tokens);
528538
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
539+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
540+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
541+
}
542+
543+
return $type;
529544
} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
530545
$type = new Ast\Type\ThisTypeNode();
546+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
547+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
548+
$tokens,
549+
$type,
550+
$startLine,
551+
$startIndex
552+
));
553+
}
554+
555+
return $type;
531556
} else {
532-
$type = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue());
533-
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
557+
$currentTokenValue = $tokens->currentTokenValue();
558+
$tokens->pushSavePoint(); // because of ConstFetchNode
559+
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
560+
$type = new Ast\Type\IdentifierTypeNode($currentTokenValue);
561+
562+
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
563+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
564+
$type = $this->parseGeneric(
565+
$tokens,
566+
$this->enrichWithAttributes(
567+
$tokens,
568+
$type,
569+
$startLine,
570+
$startIndex
571+
)
572+
);
573+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
574+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
575+
$tokens,
576+
$type,
577+
$startLine,
578+
$startIndex
579+
));
580+
}
581+
582+
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
583+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
584+
$tokens,
585+
$type,
586+
$startLine,
587+
$startIndex
588+
));
589+
590+
} elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
591+
if ($type->name === 'object') {
592+
$type = $this->parseObjectShape($tokens);
593+
} else {
594+
$type = $this->parseArrayShape($tokens, $this->enrichWithAttributes(
595+
$tokens,
596+
$type,
597+
$startLine,
598+
$startIndex
599+
), $type->name);
600+
}
601+
602+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
603+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
604+
$tokens,
605+
$type,
606+
$startLine,
607+
$startIndex
608+
));
609+
}
610+
}
534611

535-
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
536-
$type = $this->parseGeneric(
537-
$tokens,
538-
$this->enrichWithAttributes(
539-
$tokens,
540-
$type,
541-
$startLine,
542-
$startIndex
543-
)
544-
);
545-
546-
} elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
547-
if ($type->name === 'object') {
548-
$type = $this->parseObjectShape($tokens);
612+
return $type;
549613
} else {
550-
$type = $this->parseArrayShape($tokens, $this->enrichWithAttributes(
551-
$tokens,
552-
$type,
553-
$startLine,
554-
$startIndex
555-
), $type->name);
614+
$tokens->rollback(); // because of ConstFetchNode
556615
}
616+
} else {
617+
$tokens->dropSavePoint(); // because of ConstFetchNode
557618
}
558619
}
559620

560-
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
561-
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
562-
$tokens,
563-
$type,
564-
$startLine,
565-
$startIndex
566-
));
621+
$exception = new ParserException(
622+
$tokens->currentTokenValue(),
623+
$tokens->currentTokenType(),
624+
$tokens->currentTokenOffset(),
625+
Lexer::TOKEN_IDENTIFIER,
626+
null,
627+
$tokens->currentTokenLine()
628+
);
629+
630+
if ($this->constExprParser === null) {
631+
throw $exception;
567632
}
568633

569-
return $type;
634+
try {
635+
$constExpr = $this->constExprParser->parse($tokens, true);
636+
if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
637+
throw $exception;
638+
}
639+
640+
$type = new Ast\Type\ConstTypeNode($constExpr);
641+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
642+
$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
643+
$tokens,
644+
$type,
645+
$startLine,
646+
$startIndex
647+
));
648+
}
649+
650+
return $type;
651+
} catch (LogicException $e) {
652+
throw $exception;
653+
}
570654
}
571655

572656

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

+49
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,22 @@ public function provideParseData(): array
770770
)
771771
),
772772
],
773+
[
774+
'callable(): Foo<Bar>[]',
775+
new CallableTypeNode(
776+
new IdentifierTypeNode('callable'),
777+
[],
778+
new ArrayTypeNode(new GenericTypeNode(
779+
new IdentifierTypeNode('Foo'),
780+
[
781+
new IdentifierTypeNode('Bar'),
782+
],
783+
[
784+
GenericTypeNode::VARIANCE_INVARIANT,
785+
]
786+
))
787+
),
788+
],
773789
[
774790
'callable(): Foo|Bar',
775791
new UnionTypeNode([
@@ -1965,6 +1981,39 @@ public function provideParseData(): array
19651981
'callable(): $this[]',
19661982
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ThisTypeNode())),
19671983
],
1984+
[
1985+
'2.5|3',
1986+
new UnionTypeNode([
1987+
new ConstTypeNode(new ConstExprFloatNode('2.5')),
1988+
new ConstTypeNode(new ConstExprIntegerNode('3')),
1989+
]),
1990+
],
1991+
[
1992+
'callable(): 3.5',
1993+
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstExprFloatNode('3.5'))),
1994+
],
1995+
[
1996+
'callable(): 3.5[]',
1997+
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(
1998+
new ConstTypeNode(new ConstExprFloatNode('3.5'))
1999+
)),
2000+
],
2001+
[
2002+
'callable(): Foo',
2003+
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new IdentifierTypeNode('Foo')),
2004+
],
2005+
[
2006+
'callable(): (Foo)[]',
2007+
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('Foo'))),
2008+
],
2009+
[
2010+
'callable(): Foo::BAR',
2011+
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstFetchNode('Foo', 'BAR'))),
2012+
],
2013+
[
2014+
'callable(): Foo::*',
2015+
new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstFetchNode('Foo', '*'))),
2016+
],
19682017
];
19692018
}
19702019

0 commit comments

Comments
 (0)