@@ -94,7 +94,10 @@ final class TypeResolver
9494 'iterable ' => Iterable_::class,
9595 ];
9696
97- /** @var FqsenResolver */
97+ /**
98+ * @var FqsenResolver
99+ * @psalm-readonly
100+ */
98101 private $ fqsenResolver ;
99102
100103 /**
@@ -120,6 +123,8 @@ public function __construct(?FqsenResolver $fqsenResolver = null)
120123 * @uses Context::getNamespace() to determine with what to prefix the type name.
121124 *
122125 * @param string $type The relative or absolute type.
126+ *
127+ * @psalm-pure
123128 */
124129 public function resolve (string $ type , ?Context $ context = null ) : Type
125130 {
@@ -144,6 +149,7 @@ public function resolve(string $type, ?Context $context = null) : Type
144149 throw new InvalidArgumentException ('Unable to split the type string " ' . $ type . '" into tokens ' );
145150 }
146151
152+ /** @var ArrayIterator<int, string|null> $tokenIterator */
147153 $ tokenIterator = new ArrayIterator ($ tokens );
148154
149155 return $ this ->parseTypes ($ tokenIterator , $ context , self ::PARSER_IN_COMPOUND );
@@ -152,9 +158,11 @@ public function resolve(string $type, ?Context $context = null) : Type
152158 /**
153159 * Analyse each tokens and creates types
154160 *
155- * @param ArrayIterator<int, string> $tokens the iterator on tokens
161+ * @param ArrayIterator<int, string|null > $tokens the iterator on tokens
156162 * @param int $parserContext on of self::PARSER_* constants, indicating
157163 * the context where we are in the parsing
164+ *
165+ * @psalm-pure
158166 */
159167 private function parseTypes (ArrayIterator $ tokens , Context $ context , int $ parserContext ) : Type
160168 {
@@ -163,7 +171,11 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser
163171 $ compoundToken = '| ' ;
164172 while ($ tokens ->valid ()) {
165173 $ token = $ tokens ->current ();
166- if ($ token === '| ' || $ token === '& ' ) {
174+ if ($ token === null ) {
175+ throw new RuntimeException (
176+ 'Unexpected nullable character '
177+ );
178+ } elseif ($ token === '| ' || $ token === '& ' ) {
167179 if (count ($ types ) === 0 ) {
168180 throw new RuntimeException (
169181 'A type is missing before a type separator '
@@ -293,6 +305,8 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser
293305 * @param string $type the type string, representing a single type
294306 *
295307 * @return Type|Array_|Object_
308+ *
309+ * @psalm-pure
296310 */
297311 private function resolveSingleType (string $ type , Context $ context ) : object
298312 {
@@ -342,6 +356,8 @@ public function addKeyword(string $keyword, string $typeClassName) : void
342356 * Detects whether the given type represents a PHPDoc keyword.
343357 *
344358 * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
359+ *
360+ * @psalm-pure
345361 */
346362 private function isKeyword (string $ type ) : bool
347363 {
@@ -352,6 +368,8 @@ private function isKeyword(string $type) : bool
352368 * Detects whether the given type represents a relative structural element name.
353369 *
354370 * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
371+ *
372+ * @psalm-pure
355373 */
356374 private function isPartialStructuralElementName (string $ type ) : bool
357375 {
@@ -360,6 +378,8 @@ private function isPartialStructuralElementName(string $type) : bool
360378
361379 /**
362380 * Tests whether the given type is a Fully Qualified Structural Element Name.
381+ *
382+ * @psalm-pure
363383 */
364384 private function isFqsen (string $ type ) : bool
365385 {
@@ -368,6 +388,8 @@ private function isFqsen(string $type) : bool
368388
369389 /**
370390 * Resolves the given keyword (such as `string`) into a Type object representing that keyword.
391+ *
392+ * @psalm-pure
371393 */
372394 private function resolveKeyword (string $ type ) : Type
373395 {
@@ -378,6 +400,8 @@ private function resolveKeyword(string $type) : Type
378400
379401 /**
380402 * Resolves the given FQSEN string into an FQSEN object.
403+ *
404+ * @psalm-pure
381405 */
382406 private function resolveTypedObject (string $ type , ?Context $ context = null ) : Object_
383407 {
@@ -387,7 +411,9 @@ private function resolveTypedObject(string $type, ?Context $context = null) : Ob
387411 /**
388412 * Resolves class string
389413 *
390- * @param ArrayIterator<int, string> $tokens
414+ * @param ArrayIterator<int, null|string> $tokens
415+ *
416+ * @psalm-pure
391417 */
392418 private function resolveClassString (ArrayIterator $ tokens , Context $ context ) : Type
393419 {
@@ -401,15 +427,16 @@ private function resolveClassString(ArrayIterator $tokens, Context $context) : T
401427 );
402428 }
403429
404- if ($ tokens ->current () !== '> ' ) {
405- if (empty ($ tokens ->current ())) {
430+ $ token = $ tokens ->current ();
431+ if ($ token !== '> ' ) {
432+ if (empty ($ token )) {
406433 throw new RuntimeException (
407434 'class-string: ">" is missing '
408435 );
409436 }
410437
411438 throw new RuntimeException (
412- 'Unexpected character " ' . $ tokens -> current () . '", ">" is missing '
439+ 'Unexpected character " ' . $ token . '", ">" is missing '
413440 );
414441 }
415442
@@ -419,9 +446,11 @@ private function resolveClassString(ArrayIterator $tokens, Context $context) : T
419446 /**
420447 * Resolves the collection values and keys
421448 *
422- * @param ArrayIterator<int, string> $tokens
449+ * @param ArrayIterator<int, null| string> $tokens
423450 *
424451 * @return Array_|Iterable_|Collection
452+ *
453+ * @psalm-mutation-free
425454 */
426455 private function resolveCollection (ArrayIterator $ tokens , Type $ classType , Context $ context ) : Type
427456 {
@@ -441,7 +470,8 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
441470 $ valueType = $ this ->parseTypes ($ tokens , $ context , self ::PARSER_IN_COLLECTION_EXPRESSION );
442471 $ keyType = null ;
443472
444- if ($ tokens ->current () !== null && trim ($ tokens ->current ()) === ', ' ) {
473+ $ token = $ tokens ->current ();
474+ if ($ token !== null && trim ($ token ) === ', ' ) {
445475 // if we have a comma, then we just parsed the key type, not the value type
446476 $ keyType = $ valueType ;
447477 if ($ isArray ) {
@@ -474,15 +504,16 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
474504 $ valueType = $ this ->parseTypes ($ tokens , $ context , self ::PARSER_IN_COLLECTION_EXPRESSION );
475505 }
476506
477- if ($ tokens ->current () !== '> ' ) {
478- if (empty ($ tokens ->current ())) {
507+ $ token = $ tokens ->current ();
508+ if ($ token !== '> ' ) {
509+ if (empty ($ token )) {
479510 throw new RuntimeException (
480511 'Collection: ">" is missing '
481512 );
482513 }
483514
484515 throw new RuntimeException (
485- 'Unexpected character " ' . $ tokens -> current () . '", ">" is missing '
516+ 'Unexpected character " ' . $ token . '", ">" is missing '
486517 );
487518 }
488519
@@ -501,6 +532,9 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
501532 throw new RuntimeException ('Invalid $classType provided ' );
502533 }
503534
535+ /**
536+ * @psalm-pure
537+ */
504538 private function makeCollectionFromObject (Object_ $ object , Type $ valueType , ?Type $ keyType = null ) : Collection
505539 {
506540 return new Collection ($ object ->getFqsen (), $ valueType , $ keyType );
0 commit comments