22
33declare (strict_types=1 );
44
5- namespace Typhoon \PHPStanPhpDocParserBridge \Internal ;
5+ namespace Typhoon \PHPStanTypeParser \Internal ;
66
77use PHPStan \PhpDocParser \Ast \ConstExpr \ConstExprFalseNode ;
88use PHPStan \PhpDocParser \Ast \ConstExpr \ConstExprFloatNode ;
1717use PHPStan \PhpDocParser \Ast \Type \NullableTypeNode ;
1818use PHPStan \PhpDocParser \Ast \Type \TypeNode ;
1919use PHPStan \PhpDocParser \Ast \Type \UnionTypeNode ;
20+ use Typhoon \PHPStanTypeParser \CustomTypeParser ;
21+ use Typhoon \PHPStanTypeParser \TypeContext ;
2022use Typhoon \Type \Type ;
2123use function Typhoon \Type \andT ;
22- use function Typhoon \Type \diffT ;
2324use function Typhoon \Type \floatRangeT ;
2425use function Typhoon \Type \floatT ;
2526use function Typhoon \Type \intRangeT ;
4849
4950/**
5051 * @internal
51- * @psalm-internal Typhoon\PHPStanPhpDocParserBridge
52+ * @psalm-internal Typhoon\PHPStanTypeParser
5253 */
53- final class TypeConverter
54+ final class ContextualTypeParser
5455{
55- public function convert (TypeNode $ node ): Type
56+ public function __construct (
57+ private readonly CustomTypeParser $ customTypeParser ,
58+ private readonly TypeContext $ context ,
59+ ) {}
60+
61+ public function parseTypeNode (TypeNode $ node ): Type
5662 {
5763 if ($ node instanceof NullableTypeNode) {
58- return nullOrT ($ this ->convert ($ node ->type ));
64+ return nullOrT ($ this ->parseTypeNode ($ node ->type ));
5965 }
6066
6167 if ($ node instanceof ConstTypeNode) {
62- return $ this ->reflectConstExpr ($ node );
68+ return $ this ->parseConstExpr ($ node );
6369 }
6470
6571 if ($ node instanceof IdentifierTypeNode) {
66- return $ this ->reflectIdentifier ($ node ->name );
72+ return $ this ->parseIdentifier ($ node ->name );
6773 }
6874
6975 if ($ node instanceof GenericTypeNode) {
70- return $ this ->reflectIdentifier ($ node ->type ->name , $ node ->genericTypes );
76+ return $ this ->parseIdentifier ($ node ->type ->name , $ node ->genericTypes );
7177 }
7278
7379 if ($ node instanceof UnionTypeNode) {
74- return orT (...array_map ($ this ->convert (...), $ node ->types ));
80+ return orT (...array_map ($ this ->parseTypeNode (...), $ node ->types ));
7581 }
7682
7783 if ($ node instanceof IntersectionTypeNode) {
78- return andT (...array_map ($ this ->convert (...), $ node ->types ));
84+ return andT (...array_map ($ this ->parseTypeNode (...), $ node ->types ));
7985 }
8086
8187 throw new \LogicException (\sprintf ('`%s` is not supported ' , $ node ::class));
@@ -85,8 +91,10 @@ public function convert(TypeNode $node): Type
8591 * @param non-empty-string $name
8692 * @param list<TypeNode> $genericNodes
8793 */
88- private function reflectIdentifier (string $ name , array $ genericNodes = []): Type
94+ private function parseIdentifier (string $ name , array $ genericNodes = []): Type
8995 {
96+ $ typeArguments = fn (): array => array_map ($ this ->parseTypeNode (...), $ genericNodes );
97+
9098 return match ($ name ) {
9199 'never ' => neverT,
92100 'void ' => voidT,
@@ -102,8 +110,8 @@ private function reflectIdentifier(string $name, array $genericNodes = []): Type
102110 'int ' , 'integer ' => match (\count ($ genericNodes )) {
103111 0 => intT,
104112 2 => intRangeT (
105- min: $ this ->reflectRangeLimit ($ genericNodes [0 ], 'min ' , float: false ),
106- max: $ this ->reflectRangeLimit ($ genericNodes [1 ], 'max ' , float: false ),
113+ min: $ this ->parseRangeLimit ($ genericNodes [0 ], 'min ' , float: false ),
114+ max: $ this ->parseRangeLimit ($ genericNodes [1 ], 'max ' , float: false ),
107115 ),
108116 default => throw new \LogicException (\sprintf (
109117 'int range type should have 2 type arguments, got %d ' ,
@@ -113,30 +121,30 @@ private function reflectIdentifier(string $name, array $genericNodes = []): Type
113121 'float ' => match (\count ($ genericNodes )) {
114122 0 => floatT,
115123 2 => floatRangeT (
116- min: $ this ->reflectRangeLimit ($ genericNodes [0 ], 'min ' , float: true ),
117- max: $ this ->reflectRangeLimit ($ genericNodes [1 ], 'max ' , float: true ),
124+ min: $ this ->parseRangeLimit ($ genericNodes [0 ], 'min ' , float: true ),
125+ max: $ this ->parseRangeLimit ($ genericNodes [1 ], 'max ' , float: true ),
118126 ),
119127 default => throw new \LogicException (\sprintf (
120128 'float range type should have 2 type arguments, got %d ' ,
121129 \count ($ genericNodes ),
122130 ))
123131 },
124- 'diff ' => diffT ($ this ->convert ($ genericNodes [0 ]), $ this ->convert ($ genericNodes [1 ])),
125132 'string ' => stringT,
126133 'non-empty-string ' => nonEmptyStringT,
127134 'resource ' => resourceT,
128135 'array-key ' => arrayKeyT,
129136 'scalar ' => scalarT,
130137 'mixed ' => mixedT,
131- default => throw new \LogicException (),
138+ default => $ this ->customTypeParser ->parseCustomType ($ name , $ typeArguments (), $ this ->context )
139+ ?? throw new \LogicException (\sprintf ('Unknown identifier `%s` ' , $ name )),
132140 };
133141 }
134142
135143 /**
136144 * @param 'min'|'max' $name
137145 * @return ?numeric-string
138146 */
139- private function reflectRangeLimit (TypeNode $ type , string $ name , bool $ float ): ?string
147+ private function parseRangeLimit (TypeNode $ type , string $ name , bool $ float ): ?string
140148 {
141149 if ($ type instanceof IdentifierTypeNode) {
142150 if ($ type ->name === $ name ) {
@@ -163,7 +171,7 @@ private function reflectRangeLimit(TypeNode $type, string $name, bool $float): ?
163171 throw new \LogicException ();
164172 }
165173
166- private function reflectConstExpr (ConstTypeNode $ node ): Type
174+ private function parseConstExpr (ConstTypeNode $ node ): Type
167175 {
168176 $ exprNode = $ node ->constExpr ;
169177
0 commit comments