2222use Typhoon \PHPStanTypeParser \TypeContext ;
2323use Typhoon \Type \Type ;
2424use function Typhoon \Type \andT ;
25+ use function Typhoon \Type \arrayT ;
2526use function Typhoon \Type \floatRangeT ;
2627use function Typhoon \Type \floatT ;
2728use function Typhoon \Type \intRangeT ;
3031use function Typhoon \Type \orT ;
3132use function Typhoon \Type \stringT ;
3233use const Typhoon \Type \arrayKeyT ;
34+ use const Typhoon \Type \arrayT ;
3335use const Typhoon \Type \boolT ;
3436use const Typhoon \Type \falseT ;
3537use const Typhoon \Type \floatT ;
5860 */
5961final class ContextualTypeParser
6062{
61- /**
62- * @var ?non-empty-array<non-empty-string, Type|\Closure(list<TypeNode>): Type>
63- */
64- private static ?array $ identifierMap = null ;
65-
6663 public function __construct (
6764 private readonly CustomTypeParser $ customTypeParser ,
6865 private readonly TypeContext $ context ,
@@ -106,22 +103,18 @@ private static function parseConstExpr(ConstExprNode $node): Type
106103 */
107104 private function parseIdentifier (string $ name , array $ genericNodes = []): Type
108105 {
109- self :: $ identifierMap ??= [
106+ $ atomic = match ( $ name ) {
110107 'never ' => neverT,
111108 'void ' => voidT,
112109 'null ' => nullT,
113110 'false ' => falseT,
114111 'true ' => trueT,
115- 'bool ' => boolT,
116- 'boolean ' => boolT,
117- 'int ' => self ::parseInt (...),
118- 'integer ' => self ::parseInt (...),
112+ 'bool ' , 'boolean ' => boolT,
119113 'positive-int ' => positiveIntT,
120114 'negative-int ' => negativeIntT,
121115 'non-negative-int ' => nonNegativeIntT,
122116 'non-positive-int ' => nonPositiveIntT,
123117 'non-zero-int ' => nonZeroIntT,
124- 'float ' => self ::parseFloat (...),
125118 'non-empty-string ' => nonEmptyStringT,
126119 'lowercase-string ' => lowercaseStringT,
127120 'numeric-string ' => numericStringT,
@@ -131,32 +124,44 @@ private function parseIdentifier(string $name, array $genericNodes = []): Type
131124 'numeric ' => numericT,
132125 'scalar ' => scalarT,
133126 'mixed ' => mixedT,
134- ];
135-
136- $ type = self ::$ identifierMap [$ name ] ?? null ;
127+ default => null ,
128+ };
137129
138- if ($ type instanceof Type ) {
130+ if ($ atomic !== null ) {
139131 if ($ genericNodes !== []) {
140132 throw new \LogicException ();
141133 }
142134
143- return $ type ;
135+ return $ atomic ;
144136 }
145137
146- if ($ type instanceof \Closure) {
147- return $ type ($ genericNodes );
138+ if ($ name === 'int ' || $ name === 'integer ' ) {
139+ return $ this ->parseInt ($ genericNodes );
140+ }
141+
142+ if ($ name === 'float ' ) {
143+ return $ this ->parseFloat ($ genericNodes );
148144 }
149145
150146 $ templateArguments = array_map ($ this ->parseTypeNode (...), $ genericNodes );
151147
148+ if ($ name === 'array ' ) {
149+ return match ($ number = \count ($ templateArguments )) {
150+ 0 => arrayT,
151+ 1 => arrayT (valueType: $ templateArguments [0 ]),
152+ 2 => arrayT ($ templateArguments [0 ], $ templateArguments [1 ]),
153+ default => throw new \LogicException (\sprintf ('array type should have at most 2 type arguments, got %d ' , $ number )),
154+ };
155+ }
156+
152157 return $ this ->customTypeParser ->parseCustomType ($ name , $ templateArguments , $ this ->context )
153158 ?? $ this ->context ->resolveNameAsType ($ name , $ templateArguments );
154159 }
155160
156161 /**
157162 * @param list<TypeNode> $genericNodes
158163 */
159- private static function parseInt (array $ genericNodes ): Type
164+ private function parseInt (array $ genericNodes ): Type
160165 {
161166 return match (\count ($ genericNodes )) {
162167 0 => intT,
@@ -174,7 +179,7 @@ private static function parseInt(array $genericNodes): Type
174179 /**
175180 * @param list<TypeNode> $genericNodes
176181 */
177- private static function parseFloat (array $ genericNodes ): Type
182+ private function parseFloat (array $ genericNodes ): Type
178183 {
179184 return match (\count ($ genericNodes )) {
180185 0 => floatT,
@@ -193,7 +198,7 @@ private static function parseFloat(array $genericNodes): Type
193198 * @param 'min'|'max' $name
194199 * @return ?numeric-string
195200 */
196- private static function parseRangeLimit (TypeNode $ type , string $ name , bool $ float ): ?string
201+ private function parseRangeLimit (TypeNode $ type , string $ name , bool $ float ): ?string
197202 {
198203 if ($ type instanceof IdentifierTypeNode) {
199204 if ($ type ->name === $ name ) {
0 commit comments