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