1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\PhpDoc;
4:
5: use Closure;
6: use Generator;
7: use Iterator;
8: use IteratorAggregate;
9: use Nette\Utils\Strings;
10: use PhpParser\Node\Name;
11: use PHPStan\Analyser\ConstantResolver;
12: use PHPStan\Analyser\NameScope;
13: use PHPStan\DependencyInjection\AutowiredService;
14: use PHPStan\PhpDoc\Tag\TemplateTag;
15: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode;
16: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode;
17: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
18: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
19: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNullNode;
20: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
21: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode;
22: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
23: use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
24: use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
25: use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
26: use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
27: use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode;
28: use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode;
29: use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
30: use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
31: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
32: use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
33: use PHPStan\PhpDocParser\Ast\Type\InvalidTypeNode;
34: use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
35: use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode;
36: use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
37: use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
38: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
39: use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
40: use PHPStan\Reflection\Callables\SimpleImpurePoint;
41: use PHPStan\Reflection\InitializerExprContext;
42: use PHPStan\Reflection\InitializerExprTypeResolver;
43: use PHPStan\Reflection\Native\NativeParameterReflection;
44: use PHPStan\Reflection\PassedByReference;
45: use PHPStan\Reflection\ReflectionProvider;
46: use PHPStan\ShouldNotHappenException;
47: use PHPStan\TrinaryLogic;
48: use PHPStan\Type\Accessory\AccessoryArrayListType;
49: use PHPStan\Type\Accessory\AccessoryLiteralStringType;
50: use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
51: use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
52: use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
53: use PHPStan\Type\Accessory\AccessoryNumericStringType;
54: use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
55: use PHPStan\Type\Accessory\NonEmptyArrayType;
56: use PHPStan\Type\ArrayType;
57: use PHPStan\Type\BenevolentUnionType;
58: use PHPStan\Type\BooleanType;
59: use PHPStan\Type\CallableType;
60: use PHPStan\Type\ClassStringType;
61: use PHPStan\Type\ClosureType;
62: use PHPStan\Type\ConditionalType;
63: use PHPStan\Type\ConditionalTypeForParameter;
64: use PHPStan\Type\Constant\ConstantArrayType;
65: use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
66: use PHPStan\Type\Constant\ConstantBooleanType;
67: use PHPStan\Type\Constant\ConstantFloatType;
68: use PHPStan\Type\Constant\ConstantIntegerType;
69: use PHPStan\Type\Constant\ConstantStringType;
70: use PHPStan\Type\Enum\EnumCaseObjectType;
71: use PHPStan\Type\ErrorType;
72: use PHPStan\Type\FloatType;
73: use PHPStan\Type\Generic\GenericClassStringType;
74: use PHPStan\Type\Generic\GenericObjectType;
75: use PHPStan\Type\Generic\GenericStaticType;
76: use PHPStan\Type\Generic\TemplateType;
77: use PHPStan\Type\Generic\TemplateTypeFactory;
78: use PHPStan\Type\Generic\TemplateTypeMap;
79: use PHPStan\Type\Generic\TemplateTypeScope;
80: use PHPStan\Type\Generic\TemplateTypeVariance;
81: use PHPStan\Type\Helper\GetTemplateTypeType;
82: use PHPStan\Type\IntegerRangeType;
83: use PHPStan\Type\IntegerType;
84: use PHPStan\Type\IntersectionType;
85: use PHPStan\Type\IterableType;
86: use PHPStan\Type\KeyOfType;
87: use PHPStan\Type\MixedType;
88: use PHPStan\Type\NewObjectType;
89: use PHPStan\Type\NonAcceptingNeverType;
90: use PHPStan\Type\NonexistentParentClassType;
91: use PHPStan\Type\NullType;
92: use PHPStan\Type\ObjectShapeType;
93: use PHPStan\Type\ObjectType;
94: use PHPStan\Type\ObjectWithoutClassType;
95: use PHPStan\Type\OffsetAccessType;
96: use PHPStan\Type\ResourceType;
97: use PHPStan\Type\StaticType;
98: use PHPStan\Type\StaticTypeFactory;
99: use PHPStan\Type\StringAlwaysAcceptingObjectWithToStringType;
100: use PHPStan\Type\StringType;
101: use PHPStan\Type\ThisType;
102: use PHPStan\Type\Type;
103: use PHPStan\Type\TypeAliasResolver;
104: use PHPStan\Type\TypeAliasResolverProvider;
105: use PHPStan\Type\TypeCombinator;
106: use PHPStan\Type\TypeUtils;
107: use PHPStan\Type\UnionType;
108: use PHPStan\Type\ValueOfType;
109: use PHPStan\Type\VoidType;
110: use Traversable;
111: use function array_key_exists;
112: use function array_map;
113: use function array_values;
114: use function count;
115: use function explode;
116: use function get_class;
117: use function in_array;
118: use function max;
119: use function min;
120: use function preg_match;
121: use function preg_quote;
122: use function str_contains;
123: use function str_replace;
124: use function str_starts_with;
125: use function strtolower;
126: use function substr;
127:
128: #[AutowiredService]
129: final class TypeNodeResolver
130: {
131:
132: /** @var array<string, true> */
133: private array $genericTypeResolvingStack = [];
134:
135: public function __construct(
136: private TypeNodeResolverExtensionRegistryProvider $extensionRegistryProvider,
137: private ReflectionProvider\ReflectionProviderProvider $reflectionProviderProvider,
138: private TypeAliasResolverProvider $typeAliasResolverProvider,
139: private ConstantResolver $constantResolver,
140: private InitializerExprTypeResolver $initializerExprTypeResolver,
141: )
142: {
143: }
144:
145: /** @api */
146: public function resolve(TypeNode $typeNode, NameScope $nameScope): Type
147: {
148: foreach ($this->extensionRegistryProvider->getRegistry()->getExtensions() as $extension) {
149: $type = $extension->resolve($typeNode, $nameScope);
150: if ($type !== null) {
151: return $type;
152: }
153: }
154:
155: if ($typeNode instanceof IdentifierTypeNode) {
156: return $this->resolveIdentifierTypeNode($typeNode, $nameScope);
157:
158: } elseif ($typeNode instanceof ThisTypeNode) {
159: return $this->resolveThisTypeNode($typeNode, $nameScope);
160:
161: } elseif ($typeNode instanceof NullableTypeNode) {
162: return $this->resolveNullableTypeNode($typeNode, $nameScope);
163:
164: } elseif ($typeNode instanceof UnionTypeNode) {
165: return $this->resolveUnionTypeNode($typeNode, $nameScope);
166:
167: } elseif ($typeNode instanceof IntersectionTypeNode) {
168: return $this->resolveIntersectionTypeNode($typeNode, $nameScope);
169:
170: } elseif ($typeNode instanceof ConditionalTypeNode) {
171: return $this->resolveConditionalTypeNode($typeNode, $nameScope);
172:
173: } elseif ($typeNode instanceof ConditionalTypeForParameterNode) {
174: return $this->resolveConditionalTypeForParameterNode($typeNode, $nameScope);
175:
176: } elseif ($typeNode instanceof ArrayTypeNode) {
177: return $this->resolveArrayTypeNode($typeNode, $nameScope);
178:
179: } elseif ($typeNode instanceof GenericTypeNode) {
180: return $this->resolveGenericTypeNode($typeNode, $nameScope);
181:
182: } elseif ($typeNode instanceof CallableTypeNode) {
183: return $this->resolveCallableTypeNode($typeNode, $nameScope);
184:
185: } elseif ($typeNode instanceof ArrayShapeNode) {
186: return $this->resolveArrayShapeNode($typeNode, $nameScope);
187: } elseif ($typeNode instanceof ObjectShapeNode) {
188: return $this->resolveObjectShapeNode($typeNode, $nameScope);
189: } elseif ($typeNode instanceof ConstTypeNode) {
190: return $this->resolveConstTypeNode($typeNode, $nameScope);
191: } elseif ($typeNode instanceof OffsetAccessTypeNode) {
192: return $this->resolveOffsetAccessNode($typeNode, $nameScope);
193: } elseif ($typeNode instanceof InvalidTypeNode) {
194: return new MixedType(true);
195: }
196:
197: return new ErrorType();
198: }
199:
200: private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameScope $nameScope): Type
201: {
202: switch (strtolower($typeNode->name)) {
203: case 'int':
204: return new IntegerType();
205:
206: case 'integer':
207: $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope);
208:
209: if ($type !== null) {
210: return $type;
211: }
212:
213: return new IntegerType();
214:
215: case 'positive-int':
216: return IntegerRangeType::fromInterval(1, null);
217:
218: case 'negative-int':
219: return IntegerRangeType::fromInterval(null, -1);
220:
221: case 'non-positive-int':
222: return IntegerRangeType::fromInterval(null, 0);
223:
224: case 'non-negative-int':
225: return IntegerRangeType::fromInterval(0, null);
226:
227: case 'non-zero-int':
228: return new UnionType([
229: IntegerRangeType::fromInterval(null, -1),
230: IntegerRangeType::fromInterval(1, null),
231: ]);
232:
233: case 'string':
234: return new StringType();
235:
236: case 'lowercase-string':
237: return new IntersectionType([new StringType(), new AccessoryLowercaseStringType()]);
238:
239: case 'uppercase-string':
240: return new IntersectionType([new StringType(), new AccessoryUppercaseStringType()]);
241:
242: case 'literal-string':
243: return new IntersectionType([new StringType(), new AccessoryLiteralStringType()]);
244:
245: case 'class-string':
246: case 'interface-string':
247: case 'trait-string':
248: return new ClassStringType();
249:
250: case 'enum-string':
251: return new GenericClassStringType(new ObjectType('UnitEnum'));
252:
253: case 'callable-string':
254: return new IntersectionType([new StringType(), new CallableType()]);
255:
256: case 'array-key':
257: return new BenevolentUnionType([new IntegerType(), new StringType()]);
258:
259: case 'scalar':
260: $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope);
261:
262: if ($type !== null) {
263: return $type;
264: }
265:
266: return new UnionType([new IntegerType(), new FloatType(), new StringType(), new BooleanType()]);
267:
268: case 'empty-scalar':
269: return TypeCombinator::intersect(
270: new UnionType([new IntegerType(), new FloatType(), new StringType(), new BooleanType()]),
271: StaticTypeFactory::falsey(),
272: );
273:
274: case 'non-empty-scalar':
275: return TypeCombinator::remove(
276: new UnionType([new IntegerType(), new FloatType(), new StringType(), new BooleanType()]),
277: StaticTypeFactory::falsey(),
278: );
279:
280: case 'number':
281: $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope);
282:
283: if ($type !== null) {
284: return $type;
285: }
286:
287: return new UnionType([new IntegerType(), new FloatType()]);
288:
289: case 'numeric':
290: $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope);
291:
292: if ($type !== null) {
293: return $type;
294: }
295:
296: return new UnionType([
297: new IntegerType(),
298: new FloatType(),
299: new IntersectionType([
300: new StringType(),
301: new AccessoryNumericStringType(),
302: ]),
303: ]);
304:
305: case 'numeric-string':
306: return new IntersectionType([
307: new StringType(),
308: new AccessoryNumericStringType(),
309: ]);
310:
311: case 'non-empty-string':
312: return new IntersectionType([
313: new StringType(),
314: new AccessoryNonEmptyStringType(),
315: ]);
316:
317: case 'non-empty-lowercase-string':
318: return new IntersectionType([
319: new StringType(),
320: new AccessoryNonEmptyStringType(),
321: new AccessoryLowercaseStringType(),
322: ]);
323:
324: case 'non-empty-uppercase-string':
325: return new IntersectionType([
326: new StringType(),
327: new AccessoryNonEmptyStringType(),
328: new AccessoryUppercaseStringType(),
329: ]);
330:
331: case 'truthy-string':
332: case 'non-falsy-string':
333: return new IntersectionType([
334: new StringType(),
335: new AccessoryNonFalsyStringType(),
336: ]);
337:
338: case 'non-empty-literal-string':
339: return new IntersectionType([
340: new StringType(),
341: new AccessoryNonEmptyStringType(),
342: new AccessoryLiteralStringType(),
343: ]);
344:
345: case 'bool':
346: return new BooleanType();
347:
348: case 'boolean':
349: $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope);
350:
351: if ($type !== null) {
352: return $type;
353: }
354:
355: return new BooleanType();
356:
357: case 'true':
358: return new ConstantBooleanType(true);
359:
360: case 'false':
361: return new ConstantBooleanType(false);
362:
363: case 'null':
364: return new NullType();
365:
366: case 'float':
367: return new FloatType();
368:
369: case 'double':
370: $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope);
371:
372: if ($type !== null) {
373: return $type;
374: }
375:
376: return new FloatType();
377:
378: case 'array':
379: case 'associative-array':
380: return new ArrayType(new MixedType(), new MixedType());
381:
382: case 'non-empty-array':
383: return TypeCombinator::intersect(
384: new ArrayType(new MixedType(), new MixedType()),
385: new NonEmptyArrayType(),
386: );
387:
388: case 'iterable':
389: return new IterableType(new MixedType(), new MixedType());
390:
391: case 'callable':
392: return new CallableType();
393:
394: case 'pure-callable':
395: return new CallableType(isPure: TrinaryLogic::createYes());
396:
397: case 'pure-closure':
398: return ClosureType::createPure();
399:
400: case 'resource':
401: $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope);
402:
403: if ($type !== null) {
404: return $type;
405: }
406:
407: return new ResourceType();
408:
409: case 'open-resource':
410: case 'closed-resource':
411: return new ResourceType();
412:
413: case 'mixed':
414: return new MixedType(true);
415:
416: case 'non-empty-mixed':
417: return new MixedType(true, StaticTypeFactory::falsey());
418:
419: case 'void':
420: return new VoidType();
421:
422: case 'object':
423: return new ObjectWithoutClassType();
424:
425: case 'callable-object':
426: return new IntersectionType([new ObjectWithoutClassType(), new CallableType()]);
427:
428: case 'callable-array':
429: return new IntersectionType([new ArrayType(new MixedType(), new MixedType()), new CallableType()]);
430:
431: case 'never':
432: case 'noreturn':
433: $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope);
434:
435: if ($type !== null) {
436: return $type;
437: }
438:
439: return new NonAcceptingNeverType();
440:
441: case 'never-return':
442: case 'never-returns':
443: case 'no-return':
444: return new NonAcceptingNeverType();
445:
446: case 'list':
447: return TypeCombinator::intersect(new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new MixedType()), new AccessoryArrayListType());
448: case 'non-empty-list':
449: return TypeCombinator::intersect(
450: new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new MixedType()),
451: new NonEmptyArrayType(),
452: new AccessoryArrayListType(),
453: );
454:
455: case 'empty':
456: $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope);
457: if ($type !== null) {
458: return $type;
459: }
460:
461: return StaticTypeFactory::falsey();
462: case '__stringandstringable':
463: return new StringAlwaysAcceptingObjectWithToStringType();
464: }
465:
466: if ($nameScope->getClassName() !== null) {
467: switch (strtolower($typeNode->name)) {
468: case 'self':
469: return new ObjectType($nameScope->getClassName());
470:
471: case 'static':
472: if ($this->getReflectionProvider()->hasClass($nameScope->getClassName())) {
473: $classReflection = $this->getReflectionProvider()->getClass($nameScope->getClassName());
474:
475: return new StaticType($classReflection);
476: }
477:
478: return new ErrorType();
479: case 'parent':
480: if ($this->getReflectionProvider()->hasClass($nameScope->getClassName())) {
481: $classReflection = $this->getReflectionProvider()->getClass($nameScope->getClassName());
482: if ($classReflection->getParentClass() !== null) {
483: return new ObjectType($classReflection->getParentClass()->getName());
484: }
485: }
486:
487: return new NonexistentParentClassType();
488: }
489: }
490:
491: if (!$nameScope->shouldBypassTypeAliases()) {
492: $typeAlias = $this->getTypeAliasResolver()->resolveTypeAlias($typeNode->name, $nameScope);
493: if ($typeAlias !== null) {
494: return $typeAlias;
495: }
496: }
497:
498: $templateType = $nameScope->resolveTemplateTypeName($typeNode->name);
499: if ($templateType !== null) {
500: return $templateType;
501: }
502:
503: $stringName = $nameScope->resolveStringName($typeNode->name);
504: if (str_contains($stringName, '-') && !str_starts_with($stringName, 'OCI-')) {
505: return new ErrorType();
506: }
507:
508: if ($this->mightBeConstant($typeNode->name) && !$this->getReflectionProvider()->hasClass($stringName)) {
509: $constType = $this->tryResolveConstant($typeNode->name, $nameScope);
510: if ($constType !== null) {
511: return $constType;
512: }
513: }
514:
515: return new ObjectType($stringName);
516: }
517:
518: private function mightBeConstant(string $name): bool
519: {
520: return preg_match('((?:^|\\\\)[A-Z_][A-Z0-9_]*$)', $name) > 0;
521: }
522:
523: private function tryResolveConstant(string $name, NameScope $nameScope): ?Type
524: {
525: foreach ($nameScope->resolveConstantNames($name) as $constName) {
526: $nameNode = new Name\FullyQualified(explode('\\', $constName));
527: $constType = $this->constantResolver->resolveConstant($nameNode, null);
528: if ($constType !== null) {
529: return $constType;
530: }
531: }
532:
533: return null;
534: }
535:
536: private function tryResolvePseudoTypeClassType(IdentifierTypeNode $typeNode, NameScope $nameScope): ?Type
537: {
538: if ($nameScope->hasUseAlias($typeNode->name)) {
539: return new ObjectType($nameScope->resolveStringName($typeNode->name));
540: }
541:
542: if ($nameScope->getNamespace() === null) {
543: return null;
544: }
545:
546: $className = $nameScope->resolveStringName($typeNode->name);
547:
548: if ($this->getReflectionProvider()->hasClass($className)) {
549: return new ObjectType($className);
550: }
551:
552: return null;
553: }
554:
555: private function resolveThisTypeNode(ThisTypeNode $typeNode, NameScope $nameScope): Type
556: {
557: $className = $nameScope->getClassName();
558: if ($className !== null) {
559: if ($this->getReflectionProvider()->hasClass($className)) {
560: return new ThisType($this->getReflectionProvider()->getClass($className));
561: }
562: }
563:
564: return new ErrorType();
565: }
566:
567: private function resolveNullableTypeNode(NullableTypeNode $typeNode, NameScope $nameScope): Type
568: {
569: return TypeCombinator::union($this->resolve($typeNode->type, $nameScope), new NullType());
570: }
571:
572: private function resolveUnionTypeNode(UnionTypeNode $typeNode, NameScope $nameScope): Type
573: {
574: $iterableTypeNodes = [];
575: $otherTypeNodes = [];
576:
577: foreach ($typeNode->types as $innerTypeNode) {
578: if ($innerTypeNode instanceof ArrayTypeNode) {
579: $iterableTypeNodes[] = $innerTypeNode->type;
580: } else {
581: $otherTypeNodes[] = $innerTypeNode;
582: }
583: }
584:
585: $otherTypeTypes = $this->resolveMultiple($otherTypeNodes, $nameScope);
586: if (count($iterableTypeNodes) > 0) {
587: $arrayTypeTypes = $this->resolveMultiple($iterableTypeNodes, $nameScope);
588: $arrayTypeType = TypeCombinator::union(...$arrayTypeTypes);
589: $addArray = true;
590:
591: foreach ($otherTypeTypes as &$type) {
592: if (!$type->isIterable()->yes() || !$type->getIterableValueType()->isSuperTypeOf($arrayTypeType)->yes()) {
593: continue;
594: }
595:
596: if ($type instanceof ObjectType && !$type instanceof GenericObjectType) {
597: $type = new IntersectionType([$type, new IterableType(new MixedType(), $arrayTypeType)]);
598: } elseif ($type instanceof ArrayType) {
599: $type = new ArrayType(new MixedType(), $arrayTypeType);
600: } elseif ($type instanceof ConstantArrayType) {
601: $type = new ArrayType(new MixedType(), $arrayTypeType);
602: } elseif ($type instanceof IterableType) {
603: $type = new IterableType(new MixedType(), $arrayTypeType);
604: } else {
605: continue;
606: }
607:
608: $addArray = false;
609: }
610:
611: if ($addArray) {
612: $otherTypeTypes[] = new ArrayType(new MixedType(), $arrayTypeType);
613: }
614: }
615:
616: return TypeCombinator::union(...$otherTypeTypes);
617: }
618:
619: private function resolveIntersectionTypeNode(IntersectionTypeNode $typeNode, NameScope $nameScope): Type
620: {
621: $types = $this->resolveMultiple($typeNode->types, $nameScope);
622: return TypeCombinator::intersect(...$types);
623: }
624:
625: private function resolveConditionalTypeNode(ConditionalTypeNode $typeNode, NameScope $nameScope): Type
626: {
627: return new ConditionalType(
628: $this->resolve($typeNode->subjectType, $nameScope),
629: $this->resolve($typeNode->targetType, $nameScope),
630: $this->resolve($typeNode->if, $nameScope),
631: $this->resolve($typeNode->else, $nameScope),
632: $typeNode->negated,
633: );
634: }
635:
636: private function resolveConditionalTypeForParameterNode(ConditionalTypeForParameterNode $typeNode, NameScope $nameScope): Type
637: {
638: return new ConditionalTypeForParameter(
639: $typeNode->parameterName,
640: $this->resolve($typeNode->targetType, $nameScope),
641: $this->resolve($typeNode->if, $nameScope),
642: $this->resolve($typeNode->else, $nameScope),
643: $typeNode->negated,
644: );
645: }
646:
647: private function resolveArrayTypeNode(ArrayTypeNode $typeNode, NameScope $nameScope): Type
648: {
649: $itemType = $this->resolve($typeNode->type, $nameScope);
650: return new ArrayType(new BenevolentUnionType([new IntegerType(), new StringType()]), $itemType);
651: }
652:
653: private function resolveGenericTypeNode(GenericTypeNode $typeNode, NameScope $nameScope): Type
654: {
655: $mainTypeName = strtolower($typeNode->type->name);
656: $genericTypes = $this->resolveMultiple($typeNode->genericTypes, $nameScope);
657: $variances = array_map(
658: static function (string $variance): TemplateTypeVariance {
659: switch ($variance) {
660: case GenericTypeNode::VARIANCE_INVARIANT:
661: return TemplateTypeVariance::createInvariant();
662: case GenericTypeNode::VARIANCE_COVARIANT:
663: return TemplateTypeVariance::createCovariant();
664: case GenericTypeNode::VARIANCE_CONTRAVARIANT:
665: return TemplateTypeVariance::createContravariant();
666: case GenericTypeNode::VARIANCE_BIVARIANT:
667: return TemplateTypeVariance::createBivariant();
668: }
669: },
670: $typeNode->variances,
671: );
672:
673: if (in_array($mainTypeName, ['array', 'non-empty-array'], true)) {
674: if (count($genericTypes) === 1) { // array<ValueType>
675: $arrayType = new ArrayType(new BenevolentUnionType([new IntegerType(), new StringType()]), $genericTypes[0]);
676: } elseif (count($genericTypes) === 2) { // array<KeyType, ValueType>
677: $keyType = TypeCombinator::intersect($genericTypes[0]->toArrayKey(), new UnionType([
678: new IntegerType(),
679: new StringType(),
680: ]))->toArrayKey();
681: $finiteTypes = $keyType->getFiniteTypes();
682: if (
683: count($finiteTypes) === 1
684: && ($finiteTypes[0] instanceof ConstantStringType || $finiteTypes[0] instanceof ConstantIntegerType)
685: ) {
686: $arrayBuilder = ConstantArrayTypeBuilder::createEmpty();
687: $arrayBuilder->setOffsetValueType($finiteTypes[0], $genericTypes[1], true);
688: $arrayType = $arrayBuilder->getArray();
689: } else {
690: $arrayType = new ArrayType($keyType, $genericTypes[1]);
691: }
692: } else {
693: return new ErrorType();
694: }
695:
696: if ($mainTypeName === 'non-empty-array') {
697: return TypeCombinator::intersect($arrayType, new NonEmptyArrayType());
698: }
699:
700: return $arrayType;
701: } elseif (in_array($mainTypeName, ['list', 'non-empty-list'], true)) {
702: if (count($genericTypes) === 1) { // list<ValueType>
703: $listType = TypeCombinator::intersect(new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), $genericTypes[0]), new AccessoryArrayListType());
704: if ($mainTypeName === 'non-empty-list') {
705: return TypeCombinator::intersect($listType, new NonEmptyArrayType());
706: }
707:
708: return $listType;
709: }
710:
711: return new ErrorType();
712: } elseif ($mainTypeName === 'iterable') {
713: if (count($genericTypes) === 1) { // iterable<ValueType>
714: return new IterableType(new MixedType(true), $genericTypes[0]);
715:
716: }
717:
718: if (count($genericTypes) === 2) { // iterable<KeyType, ValueType>
719: return new IterableType($genericTypes[0], $genericTypes[1]);
720: }
721: } elseif (in_array($mainTypeName, ['class-string', 'interface-string'], true)) {
722: if (count($genericTypes) === 1) {
723: $genericType = $genericTypes[0];
724: if ($genericType->isObject()->yes() || $genericType instanceof MixedType) {
725: return new GenericClassStringType($genericType);
726: }
727: }
728:
729: return new ErrorType();
730: } elseif ($mainTypeName === 'enum-string') {
731: if (count($genericTypes) === 1) {
732: $genericType = $genericTypes[0];
733: return new GenericClassStringType(TypeCombinator::intersect($genericType, new ObjectType('UnitEnum')));
734: }
735:
736: return new ErrorType();
737: } elseif ($mainTypeName === 'int') {
738: if (count($genericTypes) === 2) { // int<min, max>, int<1, 3>
739:
740: if ($genericTypes[0] instanceof ConstantIntegerType) {
741: $min = $genericTypes[0]->getValue();
742: } elseif ($typeNode->genericTypes[0] instanceof IdentifierTypeNode && $typeNode->genericTypes[0]->name === 'min') {
743: $min = null;
744: } else {
745: return new ErrorType();
746: }
747:
748: if ($genericTypes[1] instanceof ConstantIntegerType) {
749: $max = $genericTypes[1]->getValue();
750: } elseif ($typeNode->genericTypes[1] instanceof IdentifierTypeNode && $typeNode->genericTypes[1]->name === 'max') {
751: $max = null;
752: } else {
753: return new ErrorType();
754: }
755:
756: return IntegerRangeType::fromInterval($min, $max);
757: }
758: } elseif ($mainTypeName === 'key-of') {
759: if (count($genericTypes) === 1) { // key-of<ValueType>
760: $type = new KeyOfType($genericTypes[0]);
761: return $type->isResolvable() ? $type->resolve() : $type;
762: }
763:
764: return new ErrorType();
765: } elseif ($mainTypeName === 'value-of') {
766: if (count($genericTypes) === 1) { // value-of<ValueType>
767: $type = new ValueOfType($genericTypes[0]);
768:
769: return $type->isResolvable() ? $type->resolve() : $type;
770: }
771:
772: return new ErrorType();
773: } elseif ($mainTypeName === 'int-mask-of') {
774: if (count($genericTypes) === 1) { // int-mask-of<Class::CONST*>
775: $maskType = $this->expandIntMaskToType($genericTypes[0]);
776: if ($maskType !== null) {
777: return $maskType;
778: }
779: }
780:
781: return new ErrorType();
782: } elseif ($mainTypeName === 'int-mask') {
783: if (count($genericTypes) > 0) { // int-mask<1, 2, 4>
784: $maskType = $this->expandIntMaskToType(TypeCombinator::union(...$genericTypes));
785: if ($maskType !== null) {
786: return $maskType;
787: }
788: }
789:
790: return new ErrorType();
791: } elseif ($mainTypeName === '__benevolent') {
792: if (count($genericTypes) === 1) {
793: return TypeUtils::toBenevolentUnion($genericTypes[0]);
794: }
795: return new ErrorType();
796: } elseif ($mainTypeName === 'template-type') {
797: if (count($genericTypes) === 3) {
798: $result = [];
799: /** @var class-string $ancestorClassName */
800: foreach ($genericTypes[1]->getObjectClassNames() as $ancestorClassName) {
801: foreach ($genericTypes[2]->getConstantStrings() as $templateTypeName) {
802: $result[] = new GetTemplateTypeType($genericTypes[0], $ancestorClassName, $templateTypeName->getValue());
803: }
804: }
805:
806: return TypeCombinator::union(...$result);
807: }
808:
809: return new ErrorType();
810: } elseif ($mainTypeName === 'new') {
811: if (count($genericTypes) === 1) {
812: $type = new NewObjectType($genericTypes[0]);
813: return $type->isResolvable() ? $type->resolve() : $type;
814: }
815:
816: return new ErrorType();
817: } elseif ($mainTypeName === 'static') {
818: if ($nameScope->getClassName() !== null && $this->getReflectionProvider()->hasClass($nameScope->getClassName())) {
819: $classReflection = $this->getReflectionProvider()->getClass($nameScope->getClassName());
820:
821: return new GenericStaticType($classReflection, $genericTypes, null, $variances);
822: }
823:
824: return new ErrorType();
825: }
826:
827: $mainType = $this->resolveIdentifierTypeNode($typeNode->type, $nameScope);
828: $mainTypeObjectClassNames = $mainType->getObjectClassNames();
829: if (count($mainTypeObjectClassNames) > 1) {
830: if ($mainType instanceof TemplateType) {
831: return new ErrorType();
832: }
833: throw new ShouldNotHappenException();
834: }
835: $mainTypeClassName = $mainTypeObjectClassNames[0] ?? null;
836:
837: if ($mainTypeClassName !== null) {
838: if (!$this->getReflectionProvider()->hasClass($mainTypeClassName)) {
839: return new GenericObjectType($mainTypeClassName, $genericTypes, variances: $variances);
840: }
841:
842: $classReflection = $this->getReflectionProvider()->getClass($mainTypeClassName);
843: if ($classReflection->isGeneric()) {
844: $templateTypes = array_values($classReflection->getTemplateTypeMap()->getTypes());
845: for ($i = count($genericTypes), $templateTypesCount = count($templateTypes); $i < $templateTypesCount; $i++) {
846: $templateType = $templateTypes[$i];
847: if (!$templateType instanceof TemplateType || $templateType->getDefault() === null) {
848: continue;
849: }
850: $genericTypes[] = $templateType->getDefault();
851: }
852:
853: if (in_array($mainTypeClassName, [
854: Traversable::class,
855: IteratorAggregate::class,
856: Iterator::class,
857: ], true)) {
858: if (count($genericTypes) === 1) {
859: return new GenericObjectType($mainTypeClassName, [
860: new MixedType(true),
861: $genericTypes[0],
862: ], variances: [
863: TemplateTypeVariance::createInvariant(),
864: $variances[0],
865: ]);
866: }
867:
868: if (count($genericTypes) === 2) {
869: return new GenericObjectType($mainTypeClassName, [
870: $genericTypes[0],
871: $genericTypes[1],
872: ], variances: [
873: $variances[0],
874: $variances[1],
875: ]);
876: }
877: }
878: if ($mainTypeClassName === Generator::class) {
879: if (count($genericTypes) === 1) {
880: $mixed = new MixedType(true);
881: return new GenericObjectType($mainTypeClassName, [
882: $mixed,
883: $genericTypes[0],
884: $mixed,
885: $mixed,
886: ], variances: [
887: TemplateTypeVariance::createInvariant(),
888: $variances[0],
889: TemplateTypeVariance::createInvariant(),
890: TemplateTypeVariance::createInvariant(),
891: ]);
892: }
893:
894: if (count($genericTypes) === 2) {
895: $mixed = new MixedType(true);
896: return new GenericObjectType($mainTypeClassName, [
897: $genericTypes[0],
898: $genericTypes[1],
899: $mixed,
900: $mixed,
901: ], variances: [
902: $variances[0],
903: $variances[1],
904: TemplateTypeVariance::createInvariant(),
905: TemplateTypeVariance::createInvariant(),
906: ]);
907: }
908: }
909:
910: if (!$mainType->isIterable()->yes()) {
911: return new GenericObjectType($mainTypeClassName, $genericTypes, variances: $variances);
912: }
913:
914: if (
915: count($genericTypes) !== 1
916: || $classReflection->getTemplateTypeMap()->count() === 1
917: ) {
918: return new GenericObjectType($mainTypeClassName, $genericTypes, variances: $variances);
919: }
920: }
921: }
922:
923: if ($mainType->isIterable()->yes()) {
924: if ($mainTypeClassName !== null) {
925: if (isset($this->genericTypeResolvingStack[$mainTypeClassName])) {
926: return new ErrorType();
927: }
928:
929: $this->genericTypeResolvingStack[$mainTypeClassName] = true;
930: }
931:
932: try {
933: if (count($genericTypes) === 1) { // Foo<ValueType>
934: return TypeCombinator::intersect(
935: $mainType,
936: new IterableType(new MixedType(true), $genericTypes[0]),
937: );
938: }
939:
940: if (count($genericTypes) === 2) { // Foo<KeyType, ValueType>
941: return TypeCombinator::intersect(
942: $mainType,
943: new IterableType($genericTypes[0], $genericTypes[1]),
944: );
945: }
946: } finally {
947: if ($mainTypeClassName !== null) {
948: unset($this->genericTypeResolvingStack[$mainTypeClassName]);
949: }
950: }
951: }
952:
953: if ($mainTypeClassName !== null) {
954: return new GenericObjectType($mainTypeClassName, $genericTypes, variances: $variances);
955: }
956:
957: return new ErrorType();
958: }
959:
960: private function resolveCallableTypeNode(CallableTypeNode $typeNode, NameScope $nameScope): Type
961: {
962: $templateTags = [];
963:
964: if (count($typeNode->templateTypes) > 0) {
965: foreach ($typeNode->templateTypes as $templateType) {
966: $templateTags[$templateType->name] = new TemplateTag(
967: $templateType->name,
968: $templateType->bound !== null
969: ? $this->resolve($templateType->bound, $nameScope)
970: : new MixedType(),
971: $templateType->default !== null
972: ? $this->resolve($templateType->default, $nameScope)
973: : null,
974: TemplateTypeVariance::createInvariant(),
975: );
976: }
977: $templateTypeScope = TemplateTypeScope::createWithAnonymousFunction();
978:
979: $templateTypeMap = new TemplateTypeMap(array_map(
980: static fn (TemplateTag $tag): Type => TemplateTypeFactory::fromTemplateTag($templateTypeScope, $tag),
981: $templateTags,
982: ));
983:
984: $nameScope = $nameScope->withTemplateTypeMap($templateTypeMap);
985: } else {
986: $templateTypeMap = TemplateTypeMap::createEmpty();
987: }
988:
989: $mainType = $this->resolve($typeNode->identifier, $nameScope);
990:
991: $isVariadic = false;
992: $parameters = array_values(array_map(
993: function (CallableTypeParameterNode $parameterNode) use ($nameScope, &$isVariadic): NativeParameterReflection {
994: $isVariadic = $isVariadic || $parameterNode->isVariadic;
995: $parameterName = $parameterNode->parameterName;
996: if (str_starts_with($parameterName, '$')) {
997: $parameterName = substr($parameterName, 1);
998: }
999:
1000: return new NativeParameterReflection(
1001: $parameterName,
1002: $parameterNode->isOptional || $parameterNode->isVariadic,
1003: $this->resolve($parameterNode->type, $nameScope),
1004: $parameterNode->isReference ? PassedByReference::createCreatesNewVariable() : PassedByReference::createNo(),
1005: $parameterNode->isVariadic,
1006: null,
1007: );
1008: },
1009: $typeNode->parameters,
1010: ));
1011:
1012: $returnType = $this->resolve($typeNode->returnType, $nameScope);
1013:
1014: if ($mainType instanceof CallableType) {
1015: $pure = $mainType->isPure();
1016: if ($pure->yes() && $returnType->isVoid()->yes()) {
1017: return new ErrorType();
1018: }
1019:
1020: return new CallableType($parameters, $returnType, $isVariadic, $templateTypeMap, templateTags: $templateTags, isPure: $pure);
1021:
1022: } elseif (
1023: $mainType instanceof ObjectType
1024: && $mainType->getClassName() === Closure::class
1025: ) {
1026: return new ClosureType($parameters, $returnType, $isVariadic, $templateTypeMap, templateTags: $templateTags, impurePoints: [
1027: new SimpleImpurePoint(
1028: 'functionCall',
1029: 'call to a Closure',
1030: false,
1031: ),
1032: ]);
1033: } elseif ($mainType instanceof ClosureType) {
1034: $closure = new ClosureType($parameters, $returnType, $isVariadic, $templateTypeMap, templateTags: $templateTags, impurePoints: $mainType->getImpurePoints(), invalidateExpressions: $mainType->getInvalidateExpressions(), usedVariables: $mainType->getUsedVariables(), acceptsNamedArguments: $mainType->acceptsNamedArguments());
1035: if ($closure->isPure()->yes() && $returnType->isVoid()->yes()) {
1036: return new ErrorType();
1037: }
1038:
1039: return $closure;
1040: }
1041:
1042: return new ErrorType();
1043: }
1044:
1045: private function resolveArrayShapeNode(ArrayShapeNode $typeNode, NameScope $nameScope): Type
1046: {
1047: $builder = ConstantArrayTypeBuilder::createEmpty();
1048: if (count($typeNode->items) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
1049: $builder->degradeToGeneralArray(true);
1050: }
1051:
1052: foreach ($typeNode->items as $itemNode) {
1053: $offsetType = null;
1054: if ($itemNode->keyName instanceof ConstExprIntegerNode) {
1055: $offsetType = new ConstantIntegerType((int) $itemNode->keyName->value);
1056: } elseif ($itemNode->keyName instanceof IdentifierTypeNode) {
1057: $offsetType = new ConstantStringType($itemNode->keyName->name);
1058: } elseif ($itemNode->keyName instanceof ConstExprStringNode) {
1059: $offsetType = new ConstantStringType($itemNode->keyName->value);
1060: } elseif ($itemNode->keyName !== null) {
1061: throw new ShouldNotHappenException('Unsupported key node type: ' . get_class($itemNode->keyName));
1062: }
1063: $builder->setOffsetValueType($offsetType, $this->resolve($itemNode->valueType, $nameScope), $itemNode->optional);
1064: }
1065:
1066: $arrayType = $builder->getArray();
1067: if (in_array($typeNode->kind, [
1068: ArrayShapeNode::KIND_LIST,
1069: ArrayShapeNode::KIND_NON_EMPTY_LIST,
1070: ], true)) {
1071: $arrayType = TypeCombinator::intersect($arrayType, new AccessoryArrayListType());
1072: }
1073:
1074: if (in_array($typeNode->kind, [
1075: ArrayShapeNode::KIND_NON_EMPTY_ARRAY,
1076: ArrayShapeNode::KIND_NON_EMPTY_LIST,
1077: ], true)) {
1078: $arrayType = TypeCombinator::intersect($arrayType, new NonEmptyArrayType());
1079: }
1080:
1081: return $arrayType;
1082: }
1083:
1084: private function resolveObjectShapeNode(ObjectShapeNode $typeNode, NameScope $nameScope): Type
1085: {
1086: $properties = [];
1087: $optionalProperties = [];
1088: foreach ($typeNode->items as $itemNode) {
1089: if ($itemNode->keyName instanceof IdentifierTypeNode) {
1090: $propertyName = $itemNode->keyName->name;
1091: } elseif ($itemNode->keyName instanceof ConstExprStringNode) {
1092: $propertyName = $itemNode->keyName->value;
1093: }
1094:
1095: if ($itemNode->optional) {
1096: $optionalProperties[] = $propertyName;
1097: }
1098:
1099: $properties[$propertyName] = $this->resolve($itemNode->valueType, $nameScope);
1100: }
1101:
1102: return new ObjectShapeType($properties, $optionalProperties);
1103: }
1104:
1105: private function resolveConstTypeNode(ConstTypeNode $typeNode, NameScope $nameScope): Type
1106: {
1107: $constExpr = $typeNode->constExpr;
1108: if ($constExpr instanceof ConstExprArrayNode) {
1109: throw new ShouldNotHappenException(); // we prefer array shapes
1110: }
1111:
1112: if (
1113: $constExpr instanceof ConstExprFalseNode
1114: || $constExpr instanceof ConstExprTrueNode
1115: || $constExpr instanceof ConstExprNullNode
1116: ) {
1117: throw new ShouldNotHappenException(); // we prefer IdentifierTypeNode
1118: }
1119:
1120: if ($constExpr instanceof ConstFetchNode) {
1121: if ($constExpr->className === '') {
1122: throw new ShouldNotHappenException(); // global constant should get parsed as class name in IdentifierTypeNode
1123: }
1124:
1125: if ($nameScope->getClassName() !== null) {
1126: switch (strtolower($constExpr->className)) {
1127: case 'static':
1128: case 'self':
1129: $className = $nameScope->getClassName();
1130: break;
1131:
1132: case 'parent':
1133: if ($this->getReflectionProvider()->hasClass($nameScope->getClassName())) {
1134: $classReflection = $this->getReflectionProvider()->getClass($nameScope->getClassName());
1135: if ($classReflection->getParentClass() === null) {
1136: return new ErrorType();
1137:
1138: }
1139:
1140: $className = $classReflection->getParentClass()->getName();
1141: }
1142: break;
1143: }
1144: }
1145:
1146: if (!isset($className)) {
1147: $className = $nameScope->resolveStringName($constExpr->className);
1148: }
1149:
1150: if (!$this->getReflectionProvider()->hasClass($className)) {
1151: return new ErrorType();
1152: }
1153:
1154: $classReflection = $this->getReflectionProvider()->getClass($className);
1155:
1156: $constantName = $constExpr->name;
1157: if (Strings::contains($constantName, '*')) {
1158: // convert * into .*? and escape everything else so the constants can be matched against the pattern
1159: $pattern = '{^' . str_replace('\\*', '.*?', preg_quote($constantName)) . '$}D';
1160: $constantTypes = [];
1161: foreach ($classReflection->getNativeReflection()->getReflectionConstants() as $reflectionConstant) {
1162: $classConstantName = $reflectionConstant->getName();
1163: if (Strings::match($classConstantName, $pattern) === null) {
1164: continue;
1165: }
1166:
1167: if ($classReflection->isEnum() && $classReflection->hasEnumCase($classConstantName)) {
1168: $constantTypes[] = new EnumCaseObjectType($classReflection->getName(), $classConstantName);
1169: continue;
1170: }
1171:
1172: $declaringClassName = $reflectionConstant->getDeclaringClass()->getName();
1173: if (!$this->getReflectionProvider()->hasClass($declaringClassName)) {
1174: continue;
1175: }
1176:
1177: $constantTypes[] = $this->initializerExprTypeResolver->getType(
1178: $reflectionConstant->getValueExpression(),
1179: InitializerExprContext::fromClassReflection(
1180: $this->getReflectionProvider()->getClass($declaringClassName),
1181: ),
1182: );
1183: }
1184:
1185: if (count($constantTypes) === 0) {
1186: return new ErrorType();
1187: }
1188:
1189: return TypeCombinator::union(...$constantTypes);
1190: }
1191:
1192: if (!$classReflection->hasConstant($constantName)) {
1193: return new ErrorType();
1194: }
1195:
1196: if ($classReflection->isEnum() && $classReflection->hasEnumCase($constantName)) {
1197: return new EnumCaseObjectType($classReflection->getName(), $constantName);
1198: }
1199:
1200: $reflectionConstant = $classReflection->getNativeReflection()->getReflectionConstant($constantName);
1201: if ($reflectionConstant === false) {
1202: return new ErrorType();
1203: }
1204: $declaringClass = $reflectionConstant->getDeclaringClass();
1205:
1206: return $this->initializerExprTypeResolver->getType($reflectionConstant->getValueExpression(), InitializerExprContext::fromClass($declaringClass->getName(), $declaringClass->getFileName() ?: null));
1207: }
1208:
1209: if ($constExpr instanceof ConstExprFloatNode) {
1210: return new ConstantFloatType((float) $constExpr->value);
1211: }
1212:
1213: if ($constExpr instanceof ConstExprIntegerNode) {
1214: return new ConstantIntegerType((int) $constExpr->value);
1215: }
1216:
1217: if ($constExpr instanceof ConstExprStringNode) {
1218: return new ConstantStringType($constExpr->value);
1219: }
1220:
1221: return new ErrorType();
1222: }
1223:
1224: private function resolveOffsetAccessNode(OffsetAccessTypeNode $typeNode, NameScope $nameScope): Type
1225: {
1226: $type = $this->resolve($typeNode->type, $nameScope);
1227: $offset = $this->resolve($typeNode->offset, $nameScope);
1228:
1229: if ($type->isOffsetAccessible()->no() || $type->hasOffsetValueType($offset)->no()) {
1230: return new ErrorType();
1231: }
1232:
1233: return new OffsetAccessType($type, $offset);
1234: }
1235:
1236: private function expandIntMaskToType(Type $type): ?Type
1237: {
1238: $ints = array_map(static fn (ConstantIntegerType $type) => $type->getValue(), TypeUtils::getConstantIntegers($type));
1239: if (count($ints) === 0) {
1240: return null;
1241: }
1242:
1243: $values = [];
1244:
1245: foreach ($ints as $int) {
1246: if ($int !== 0 && !array_key_exists($int, $values)) {
1247: foreach ($values as $value) {
1248: $computedValue = $value | $int;
1249: $values[$computedValue] = $computedValue;
1250: }
1251: }
1252:
1253: $values[$int] = $int;
1254: }
1255:
1256: $values[0] = 0;
1257:
1258: $min = min($values);
1259: $max = max($values);
1260:
1261: if ($max - $min === count($values) - 1) {
1262: return IntegerRangeType::fromInterval($min, $max);
1263: }
1264:
1265: if (count($values) > InitializerExprTypeResolver::CALCULATE_SCALARS_LIMIT) {
1266: return IntegerRangeType::fromInterval($min, $max);
1267: }
1268:
1269: return TypeCombinator::union(...array_map(static fn ($value) => new ConstantIntegerType($value), $values));
1270: }
1271:
1272: /**
1273: * @api
1274: * @param TypeNode[] $typeNodes
1275: * @return list<Type>
1276: */
1277: public function resolveMultiple(array $typeNodes, NameScope $nameScope): array
1278: {
1279: $types = [];
1280: foreach ($typeNodes as $typeNode) {
1281: $types[] = $this->resolve($typeNode, $nameScope);
1282: }
1283:
1284: return $types;
1285: }
1286:
1287: private function getReflectionProvider(): ReflectionProvider
1288: {
1289: return $this->reflectionProviderProvider->getReflectionProvider();
1290: }
1291:
1292: private function getTypeAliasResolver(): TypeAliasResolver
1293: {
1294: return $this->typeAliasResolverProvider->getTypeAliasResolver();
1295: }
1296:
1297: }
1298: