@@ -93,7 +93,10 @@ final class TypeResolver
9393 'iterable ' => Iterable_::class,
9494 ];
9595
96- /** @var FqsenResolver */
96+ /**
97+ * @var FqsenResolver
98+ * @psalm-readonly
99+ */
97100 private $ fqsenResolver ;
98101
99102 /**
@@ -119,6 +122,8 @@ public function __construct(?FqsenResolver $fqsenResolver = null)
119122 * @uses Context::getNamespace() to determine with what to prefix the type name.
120123 *
121124 * @param string $type The relative or absolute type.
125+ *
126+ * @psalm-pure
122127 */
123128 public function resolve (string $ type , ?Context $ context = null ) : Type
124129 {
@@ -143,6 +148,7 @@ public function resolve(string $type, ?Context $context = null) : Type
143148 throw new InvalidArgumentException ('Unable to split the type string " ' . $ type . '" into tokens ' );
144149 }
145150
151+ /** @var ArrayIterator<int, string|null> $tokenIterator */
146152 $ tokenIterator = new ArrayIterator ($ tokens );
147153
148154 return $ this ->parseTypes ($ tokenIterator , $ context , self ::PARSER_IN_COMPOUND );
@@ -151,18 +157,23 @@ public function resolve(string $type, ?Context $context = null) : Type
151157 /**
152158 * Analyse each tokens and creates types
153159 *
154- * @param ArrayIterator<int, string> $tokens the iterator on tokens
160+ * @param ArrayIterator<int, string|null > $tokens the iterator on tokens
155161 * @param int $parserContext on of self::PARSER_* constants, indicating
156162 * the context where we are in the parsing
163+ *
164+ * @psalm-pure
157165 */
158166 private function parseTypes (ArrayIterator $ tokens , Context $ context , int $ parserContext ) : Type
159167 {
160168 $ types = [];
161169 $ token = '' ;
162170 while ($ tokens ->valid ()) {
163171 $ token = $ tokens ->current ();
164-
165- if ($ token === '| ' ) {
172+ if ($ token === null ) {
173+ throw new RuntimeException (
174+ 'Unexpected nullable character '
175+ );
176+ } elseif ($ token === '| ' ) {
166177 if (count ($ types ) === 0 ) {
167178 throw new RuntimeException (
168179 'A type is missing before a type separator '
@@ -287,6 +298,8 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser
287298 * @param string $type the type string, representing a single type
288299 *
289300 * @return Type|Array_|Object_
301+ *
302+ * @psalm-pure
290303 */
291304 private function resolveSingleType (string $ type , Context $ context ) : object
292305 {
@@ -338,6 +351,8 @@ public function addKeyword(string $keyword, string $typeClassName) : void
338351 * Detects whether the given type represents an array.
339352 *
340353 * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
354+ *
355+ * @psalm-pure
341356 */
342357 private function isTypedArray (string $ type ) : bool
343358 {
@@ -348,6 +363,8 @@ private function isTypedArray(string $type) : bool
348363 * Detects whether the given type represents a PHPDoc keyword.
349364 *
350365 * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
366+ *
367+ * @psalm-pure
351368 */
352369 private function isKeyword (string $ type ) : bool
353370 {
@@ -358,6 +375,8 @@ private function isKeyword(string $type) : bool
358375 * Detects whether the given type represents a relative structural element name.
359376 *
360377 * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
378+ *
379+ * @psalm-pure
361380 */
362381 private function isPartialStructuralElementName (string $ type ) : bool
363382 {
@@ -366,6 +385,8 @@ private function isPartialStructuralElementName(string $type) : bool
366385
367386 /**
368387 * Tests whether the given type is a Fully Qualified Structural Element Name.
388+ *
389+ * @psalm-pure
369390 */
370391 private function isFqsen (string $ type ) : bool
371392 {
@@ -374,6 +395,8 @@ private function isFqsen(string $type) : bool
374395
375396 /**
376397 * Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set.
398+ *
399+ * @psalm-pure
377400 */
378401 private function resolveTypedArray (string $ type , Context $ context ) : Array_
379402 {
@@ -382,6 +405,8 @@ private function resolveTypedArray(string $type, Context $context) : Array_
382405
383406 /**
384407 * Resolves the given keyword (such as `string`) into a Type object representing that keyword.
408+ *
409+ * @psalm-pure
385410 */
386411 private function resolveKeyword (string $ type ) : Type
387412 {
@@ -392,6 +417,8 @@ private function resolveKeyword(string $type) : Type
392417
393418 /**
394419 * Resolves the given FQSEN string into an FQSEN object.
420+ *
421+ * @psalm-pure
395422 */
396423 private function resolveTypedObject (string $ type , ?Context $ context = null ) : Object_
397424 {
@@ -401,7 +428,9 @@ private function resolveTypedObject(string $type, ?Context $context = null) : Ob
401428 /**
402429 * Resolves class string
403430 *
404- * @param ArrayIterator<int, string> $tokens
431+ * @param ArrayIterator<int, null|string> $tokens
432+ *
433+ * @psalm-pure
405434 */
406435 private function resolveClassString (ArrayIterator $ tokens , Context $ context ) : Type
407436 {
@@ -415,15 +444,16 @@ private function resolveClassString(ArrayIterator $tokens, Context $context) : T
415444 );
416445 }
417446
418- if ($ tokens ->current () !== '> ' ) {
419- if (empty ($ tokens ->current ())) {
447+ $ token = $ tokens ->current ();
448+ if ($ token !== '> ' ) {
449+ if (empty ($ token )) {
420450 throw new RuntimeException (
421451 'class-string: ">" is missing '
422452 );
423453 }
424454
425455 throw new RuntimeException (
426- 'Unexpected character " ' . $ tokens -> current () . '", ">" is missing '
456+ 'Unexpected character " ' . $ token . '", ">" is missing '
427457 );
428458 }
429459
@@ -433,9 +463,11 @@ private function resolveClassString(ArrayIterator $tokens, Context $context) : T
433463 /**
434464 * Resolves the collection values and keys
435465 *
436- * @param ArrayIterator<int, string> $tokens
466+ * @param ArrayIterator<int, null| string> $tokens
437467 *
438468 * @return Array_|Iterable_|Collection
469+ *
470+ * @psalm-mutation-free
439471 */
440472 private function resolveCollection (ArrayIterator $ tokens , Type $ classType , Context $ context ) : Type
441473 {
@@ -455,7 +487,8 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
455487 $ valueType = $ this ->parseTypes ($ tokens , $ context , self ::PARSER_IN_COLLECTION_EXPRESSION );
456488 $ keyType = null ;
457489
458- if ($ tokens ->current () !== null && trim ($ tokens ->current ()) === ', ' ) {
490+ $ token = $ tokens ->current ();
491+ if ($ token !== null && trim ($ token ) === ', ' ) {
459492 // if we have a comma, then we just parsed the key type, not the value type
460493 $ keyType = $ valueType ;
461494 if ($ isArray ) {
@@ -488,15 +521,16 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
488521 $ valueType = $ this ->parseTypes ($ tokens , $ context , self ::PARSER_IN_COLLECTION_EXPRESSION );
489522 }
490523
491- if ($ tokens ->current () !== '> ' ) {
492- if (empty ($ tokens ->current ())) {
524+ $ token = $ tokens ->current ();
525+ if ($ token !== '> ' ) {
526+ if (empty ($ token )) {
493527 throw new RuntimeException (
494528 'Collection: ">" is missing '
495529 );
496530 }
497531
498532 throw new RuntimeException (
499- 'Unexpected character " ' . $ tokens -> current () . '", ">" is missing '
533+ 'Unexpected character " ' . $ token . '", ">" is missing '
500534 );
501535 }
502536
@@ -515,6 +549,9 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
515549 throw new RuntimeException ('Invalid $classType provided ' );
516550 }
517551
552+ /**
553+ * @psalm-pure
554+ */
518555 private function makeCollectionFromObject (Object_ $ object , Type $ valueType , ?Type $ keyType = null ) : Collection
519556 {
520557 return new Collection ($ object ->getFqsen (), $ valueType , $ keyType );
0 commit comments