@@ -94,7 +94,10 @@ final class TypeResolver
94
94
'iterable ' => Iterable_::class,
95
95
];
96
96
97
- /** @var FqsenResolver */
97
+ /**
98
+ * @var FqsenResolver
99
+ * @psalm-readonly
100
+ */
98
101
private $ fqsenResolver ;
99
102
100
103
/**
@@ -120,6 +123,8 @@ public function __construct(?FqsenResolver $fqsenResolver = null)
120
123
* @uses Context::getNamespace() to determine with what to prefix the type name.
121
124
*
122
125
* @param string $type The relative or absolute type.
126
+ *
127
+ * @psalm-pure
123
128
*/
124
129
public function resolve (string $ type , ?Context $ context = null ) : Type
125
130
{
@@ -144,6 +149,7 @@ public function resolve(string $type, ?Context $context = null) : Type
144
149
throw new InvalidArgumentException ('Unable to split the type string " ' . $ type . '" into tokens ' );
145
150
}
146
151
152
+ /** @var ArrayIterator<int, string|null> $tokenIterator */
147
153
$ tokenIterator = new ArrayIterator ($ tokens );
148
154
149
155
return $ this ->parseTypes ($ tokenIterator , $ context , self ::PARSER_IN_COMPOUND );
@@ -152,9 +158,11 @@ public function resolve(string $type, ?Context $context = null) : Type
152
158
/**
153
159
* Analyse each tokens and creates types
154
160
*
155
- * @param ArrayIterator<int, string> $tokens the iterator on tokens
161
+ * @param ArrayIterator<int, string|null > $tokens the iterator on tokens
156
162
* @param int $parserContext on of self::PARSER_* constants, indicating
157
163
* the context where we are in the parsing
164
+ *
165
+ * @psalm-pure
158
166
*/
159
167
private function parseTypes (ArrayIterator $ tokens , Context $ context , int $ parserContext ) : Type
160
168
{
@@ -163,7 +171,11 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser
163
171
$ compoundToken = '| ' ;
164
172
while ($ tokens ->valid ()) {
165
173
$ token = $ tokens ->current ();
166
- if ($ token === '| ' || $ token === '& ' ) {
174
+ if ($ token === null ) {
175
+ throw new RuntimeException (
176
+ 'Unexpected nullable character '
177
+ );
178
+ } elseif ($ token === '| ' || $ token === '& ' ) {
167
179
if (count ($ types ) === 0 ) {
168
180
throw new RuntimeException (
169
181
'A type is missing before a type separator '
@@ -293,6 +305,8 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser
293
305
* @param string $type the type string, representing a single type
294
306
*
295
307
* @return Type|Array_|Object_
308
+ *
309
+ * @psalm-pure
296
310
*/
297
311
private function resolveSingleType (string $ type , Context $ context ) : object
298
312
{
@@ -342,6 +356,8 @@ public function addKeyword(string $keyword, string $typeClassName) : void
342
356
* Detects whether the given type represents a PHPDoc keyword.
343
357
*
344
358
* @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
359
+ *
360
+ * @psalm-pure
345
361
*/
346
362
private function isKeyword (string $ type ) : bool
347
363
{
@@ -352,6 +368,8 @@ private function isKeyword(string $type) : bool
352
368
* Detects whether the given type represents a relative structural element name.
353
369
*
354
370
* @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
371
+ *
372
+ * @psalm-pure
355
373
*/
356
374
private function isPartialStructuralElementName (string $ type ) : bool
357
375
{
@@ -360,6 +378,8 @@ private function isPartialStructuralElementName(string $type) : bool
360
378
361
379
/**
362
380
* Tests whether the given type is a Fully Qualified Structural Element Name.
381
+ *
382
+ * @psalm-pure
363
383
*/
364
384
private function isFqsen (string $ type ) : bool
365
385
{
@@ -368,6 +388,8 @@ private function isFqsen(string $type) : bool
368
388
369
389
/**
370
390
* Resolves the given keyword (such as `string`) into a Type object representing that keyword.
391
+ *
392
+ * @psalm-pure
371
393
*/
372
394
private function resolveKeyword (string $ type ) : Type
373
395
{
@@ -378,6 +400,8 @@ private function resolveKeyword(string $type) : Type
378
400
379
401
/**
380
402
* Resolves the given FQSEN string into an FQSEN object.
403
+ *
404
+ * @psalm-pure
381
405
*/
382
406
private function resolveTypedObject (string $ type , ?Context $ context = null ) : Object_
383
407
{
@@ -387,7 +411,9 @@ private function resolveTypedObject(string $type, ?Context $context = null) : Ob
387
411
/**
388
412
* Resolves class string
389
413
*
390
- * @param ArrayIterator<int, string> $tokens
414
+ * @param ArrayIterator<int, null|string> $tokens
415
+ *
416
+ * @psalm-pure
391
417
*/
392
418
private function resolveClassString (ArrayIterator $ tokens , Context $ context ) : Type
393
419
{
@@ -401,15 +427,16 @@ private function resolveClassString(ArrayIterator $tokens, Context $context) : T
401
427
);
402
428
}
403
429
404
- if ($ tokens ->current () !== '> ' ) {
405
- if (empty ($ tokens ->current ())) {
430
+ $ token = $ tokens ->current ();
431
+ if ($ token !== '> ' ) {
432
+ if (empty ($ token )) {
406
433
throw new RuntimeException (
407
434
'class-string: ">" is missing '
408
435
);
409
436
}
410
437
411
438
throw new RuntimeException (
412
- 'Unexpected character " ' . $ tokens -> current () . '", ">" is missing '
439
+ 'Unexpected character " ' . $ token . '", ">" is missing '
413
440
);
414
441
}
415
442
@@ -419,9 +446,11 @@ private function resolveClassString(ArrayIterator $tokens, Context $context) : T
419
446
/**
420
447
* Resolves the collection values and keys
421
448
*
422
- * @param ArrayIterator<int, string> $tokens
449
+ * @param ArrayIterator<int, null| string> $tokens
423
450
*
424
451
* @return Array_|Iterable_|Collection
452
+ *
453
+ * @psalm-mutation-free
425
454
*/
426
455
private function resolveCollection (ArrayIterator $ tokens , Type $ classType , Context $ context ) : Type
427
456
{
@@ -441,7 +470,8 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
441
470
$ valueType = $ this ->parseTypes ($ tokens , $ context , self ::PARSER_IN_COLLECTION_EXPRESSION );
442
471
$ keyType = null ;
443
472
444
- if ($ tokens ->current () !== null && trim ($ tokens ->current ()) === ', ' ) {
473
+ $ token = $ tokens ->current ();
474
+ if ($ token !== null && trim ($ token ) === ', ' ) {
445
475
// if we have a comma, then we just parsed the key type, not the value type
446
476
$ keyType = $ valueType ;
447
477
if ($ isArray ) {
@@ -474,15 +504,16 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
474
504
$ valueType = $ this ->parseTypes ($ tokens , $ context , self ::PARSER_IN_COLLECTION_EXPRESSION );
475
505
}
476
506
477
- if ($ tokens ->current () !== '> ' ) {
478
- if (empty ($ tokens ->current ())) {
507
+ $ token = $ tokens ->current ();
508
+ if ($ token !== '> ' ) {
509
+ if (empty ($ token )) {
479
510
throw new RuntimeException (
480
511
'Collection: ">" is missing '
481
512
);
482
513
}
483
514
484
515
throw new RuntimeException (
485
- 'Unexpected character " ' . $ tokens -> current () . '", ">" is missing '
516
+ 'Unexpected character " ' . $ token . '", ">" is missing '
486
517
);
487
518
}
488
519
@@ -501,6 +532,9 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
501
532
throw new RuntimeException ('Invalid $classType provided ' );
502
533
}
503
534
535
+ /**
536
+ * @psalm-pure
537
+ */
504
538
private function makeCollectionFromObject (Object_ $ object , Type $ valueType , ?Type $ keyType = null ) : Collection
505
539
{
506
540
return new Collection ($ object ->getFqsen (), $ valueType , $ keyType );
0 commit comments