1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan\PhpDoc; |
4: | |
5: | use PHPStan\Analyser\NameScope; |
6: | use PHPStan\PhpDoc\Tag\DeprecatedTag; |
7: | use PHPStan\PhpDoc\Tag\ExtendsTag; |
8: | use PHPStan\PhpDoc\Tag\ImplementsTag; |
9: | use PHPStan\PhpDoc\Tag\MethodTag; |
10: | use PHPStan\PhpDoc\Tag\MixinTag; |
11: | use PHPStan\PhpDoc\Tag\ParamTag; |
12: | use PHPStan\PhpDoc\Tag\PropertyTag; |
13: | use PHPStan\PhpDoc\Tag\ReturnTag; |
14: | use PHPStan\PhpDoc\Tag\TemplateTag; |
15: | use PHPStan\PhpDoc\Tag\ThrowsTag; |
16: | use PHPStan\PhpDoc\Tag\TypeAliasImportTag; |
17: | use PHPStan\PhpDoc\Tag\TypeAliasTag; |
18: | use PHPStan\PhpDoc\Tag\TypedTag; |
19: | use PHPStan\PhpDoc\Tag\UsesTag; |
20: | use PHPStan\PhpDoc\Tag\VarTag; |
21: | use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; |
22: | use PHPStan\Type\ConditionalTypeForParameter; |
23: | use PHPStan\Type\Generic\TemplateTypeHelper; |
24: | use PHPStan\Type\Generic\TemplateTypeMap; |
25: | use PHPStan\Type\Type; |
26: | use PHPStan\Type\TypeTraverser; |
27: | use function array_key_exists; |
28: | use function count; |
29: | use function is_bool; |
30: | use function substr; |
31: | |
32: | |
33: | class ResolvedPhpDocBlock |
34: | { |
35: | |
36: | private PhpDocNode $phpDocNode; |
37: | |
38: | |
39: | private array $phpDocNodes; |
40: | |
41: | private string $phpDocString; |
42: | |
43: | private ?string $filename; |
44: | |
45: | private ?NameScope $nameScope = null; |
46: | |
47: | private TemplateTypeMap $templateTypeMap; |
48: | |
49: | |
50: | private array $templateTags; |
51: | |
52: | private PhpDocNodeResolver $phpDocNodeResolver; |
53: | |
54: | |
55: | private array|false $varTags = false; |
56: | |
57: | |
58: | private array|false $methodTags = false; |
59: | |
60: | |
61: | private array|false $propertyTags = false; |
62: | |
63: | |
64: | private array|false $extendsTags = false; |
65: | |
66: | |
67: | private array|false $implementsTags = false; |
68: | |
69: | |
70: | private array|false $usesTags = false; |
71: | |
72: | |
73: | private array|false $paramTags = false; |
74: | |
75: | private ReturnTag|false|null $returnTag = false; |
76: | |
77: | private ThrowsTag|false|null $throwsTag = false; |
78: | |
79: | |
80: | private array|false $mixinTags = false; |
81: | |
82: | |
83: | private array|false $typeAliasTags = false; |
84: | |
85: | |
86: | private array|false $typeAliasImportTags = false; |
87: | |
88: | private DeprecatedTag|false|null $deprecatedTag = false; |
89: | |
90: | private ?bool $isDeprecated = null; |
91: | |
92: | private ?bool $isInternal = null; |
93: | |
94: | private ?bool $isFinal = null; |
95: | |
96: | |
97: | private bool|string|null $isPure = 'notLoaded'; |
98: | |
99: | private ?bool $isReadOnly = null; |
100: | |
101: | private ?bool $isImmutable = null; |
102: | |
103: | private ?bool $isAllowedPrivateMutation = null; |
104: | |
105: | private ?bool $hasConsistentConstructor = null; |
106: | |
107: | private ?bool $acceptsNamedArguments = null; |
108: | |
109: | private function __construct() |
110: | { |
111: | } |
112: | |
113: | |
114: | |
115: | |
116: | public static function create( |
117: | PhpDocNode $phpDocNode, |
118: | string $phpDocString, |
119: | ?string $filename, |
120: | NameScope $nameScope, |
121: | TemplateTypeMap $templateTypeMap, |
122: | array $templateTags, |
123: | PhpDocNodeResolver $phpDocNodeResolver, |
124: | ): self |
125: | { |
126: | |
127: | $self = new self(); |
128: | $self->phpDocNode = $phpDocNode; |
129: | $self->phpDocNodes = [$phpDocNode]; |
130: | $self->phpDocString = $phpDocString; |
131: | $self->filename = $filename; |
132: | $self->nameScope = $nameScope; |
133: | $self->templateTypeMap = $templateTypeMap; |
134: | $self->templateTags = $templateTags; |
135: | $self->phpDocNodeResolver = $phpDocNodeResolver; |
136: | |
137: | return $self; |
138: | } |
139: | |
140: | public static function createEmpty(): self |
141: | { |
142: | |
143: | $self = new self(); |
144: | $self->phpDocString = '/** */'; |
145: | $self->phpDocNodes = []; |
146: | $self->filename = null; |
147: | $self->templateTypeMap = TemplateTypeMap::createEmpty(); |
148: | $self->templateTags = []; |
149: | $self->varTags = []; |
150: | $self->methodTags = []; |
151: | $self->propertyTags = []; |
152: | $self->extendsTags = []; |
153: | $self->implementsTags = []; |
154: | $self->usesTags = []; |
155: | $self->paramTags = []; |
156: | $self->returnTag = null; |
157: | $self->throwsTag = null; |
158: | $self->mixinTags = []; |
159: | $self->typeAliasTags = []; |
160: | $self->typeAliasImportTags = []; |
161: | $self->deprecatedTag = null; |
162: | $self->isDeprecated = false; |
163: | $self->isInternal = false; |
164: | $self->isFinal = false; |
165: | $self->isPure = null; |
166: | $self->isReadOnly = false; |
167: | $self->isImmutable = false; |
168: | $self->isAllowedPrivateMutation = false; |
169: | $self->hasConsistentConstructor = false; |
170: | $self->acceptsNamedArguments = true; |
171: | |
172: | return $self; |
173: | } |
174: | |
175: | |
176: | |
177: | |
178: | |
179: | public function merge(array $parents, array $parentPhpDocBlocks): self |
180: | { |
181: | |
182: | $result = new self(); |
183: | |
184: | |
185: | |
186: | $phpDocNodes = $this->phpDocNodes; |
187: | $acceptsNamedArguments = $this->acceptsNamedArguments(); |
188: | foreach ($parents as $parent) { |
189: | foreach ($parent->phpDocNodes as $phpDocNode) { |
190: | $phpDocNodes[] = $phpDocNode; |
191: | $acceptsNamedArguments = $acceptsNamedArguments && $parent->acceptsNamedArguments(); |
192: | } |
193: | } |
194: | $result->phpDocNodes = $phpDocNodes; |
195: | $result->filename = $this->filename; |
196: | |
197: | $result->templateTypeMap = $this->templateTypeMap; |
198: | $result->templateTags = $this->templateTags; |
199: | |
200: | $result->varTags = self::mergeVarTags($this->getVarTags(), $parents, $parentPhpDocBlocks); |
201: | $result->methodTags = $this->getMethodTags(); |
202: | $result->propertyTags = $this->getPropertyTags(); |
203: | $result->extendsTags = $this->getExtendsTags(); |
204: | $result->implementsTags = $this->getImplementsTags(); |
205: | $result->usesTags = $this->getUsesTags(); |
206: | $result->paramTags = self::mergeParamTags($this->getParamTags(), $parents, $parentPhpDocBlocks); |
207: | $result->returnTag = self::mergeReturnTags($this->getReturnTag(), $parents, $parentPhpDocBlocks); |
208: | $result->throwsTag = self::mergeThrowsTags($this->getThrowsTag(), $parents); |
209: | $result->mixinTags = $this->getMixinTags(); |
210: | $result->typeAliasTags = $this->getTypeAliasTags(); |
211: | $result->typeAliasImportTags = $this->getTypeAliasImportTags(); |
212: | $result->deprecatedTag = self::mergeDeprecatedTags($this->getDeprecatedTag(), $parents); |
213: | $result->isDeprecated = $result->deprecatedTag !== null; |
214: | $result->isInternal = $this->isInternal(); |
215: | $result->isFinal = $this->isFinal(); |
216: | $result->isPure = $this->isPure(); |
217: | $result->isReadOnly = $this->isReadOnly(); |
218: | $result->isImmutable = $this->isImmutable(); |
219: | $result->isAllowedPrivateMutation = $this->isAllowedPrivateMutation(); |
220: | $result->hasConsistentConstructor = $this->hasConsistentConstructor(); |
221: | $result->acceptsNamedArguments = $acceptsNamedArguments; |
222: | |
223: | return $result; |
224: | } |
225: | |
226: | |
227: | |
228: | |
229: | public function changeParameterNamesByMapping(array $parameterNameMapping): self |
230: | { |
231: | if (count($this->phpDocNodes) === 0) { |
232: | return $this; |
233: | } |
234: | |
235: | $paramTags = $this->getParamTags(); |
236: | |
237: | $newParamTags = []; |
238: | foreach ($paramTags as $key => $paramTag) { |
239: | if (!array_key_exists($key, $parameterNameMapping)) { |
240: | continue; |
241: | } |
242: | $newParamTags[$parameterNameMapping[$key]] = $paramTag; |
243: | } |
244: | |
245: | $returnTag = $this->getReturnTag(); |
246: | if ($returnTag !== null) { |
247: | $transformedType = TypeTraverser::map($returnTag->getType(), static function (Type $type, callable $traverse) use ($parameterNameMapping): Type { |
248: | if ($type instanceof ConditionalTypeForParameter) { |
249: | $parameterName = substr($type->getParameterName(), 1); |
250: | if (array_key_exists($parameterName, $parameterNameMapping)) { |
251: | $type = $type->changeParameterName('$' . $parameterNameMapping[$parameterName]); |
252: | } |
253: | } |
254: | |
255: | return $traverse($type); |
256: | }); |
257: | $returnTag = $returnTag->withType($transformedType); |
258: | } |
259: | |
260: | $self = new self(); |
261: | $self->phpDocNode = $this->phpDocNode; |
262: | $self->phpDocNodes = $this->phpDocNodes; |
263: | $self->phpDocString = $this->phpDocString; |
264: | $self->filename = $this->filename; |
265: | $self->nameScope = $this->nameScope; |
266: | $self->templateTypeMap = $this->templateTypeMap; |
267: | $self->templateTags = $this->templateTags; |
268: | $self->phpDocNodeResolver = $this->phpDocNodeResolver; |
269: | $self->varTags = $this->varTags; |
270: | $self->methodTags = $this->methodTags; |
271: | $self->propertyTags = $this->propertyTags; |
272: | $self->extendsTags = $this->extendsTags; |
273: | $self->implementsTags = $this->implementsTags; |
274: | $self->usesTags = $this->usesTags; |
275: | $self->paramTags = $newParamTags; |
276: | $self->returnTag = $returnTag; |
277: | $self->throwsTag = $this->throwsTag; |
278: | $self->mixinTags = $this->mixinTags; |
279: | $self->typeAliasTags = $this->typeAliasTags; |
280: | $self->typeAliasImportTags = $this->typeAliasImportTags; |
281: | $self->deprecatedTag = $this->deprecatedTag; |
282: | $self->isDeprecated = $this->isDeprecated; |
283: | $self->isInternal = $this->isInternal; |
284: | $self->isFinal = $this->isFinal; |
285: | $self->isPure = $this->isPure; |
286: | |
287: | return $self; |
288: | } |
289: | |
290: | public function getPhpDocString(): string |
291: | { |
292: | return $this->phpDocString; |
293: | } |
294: | |
295: | |
296: | |
297: | |
298: | public function getPhpDocNodes(): array |
299: | { |
300: | return $this->phpDocNodes; |
301: | } |
302: | |
303: | public function getFilename(): ?string |
304: | { |
305: | return $this->filename; |
306: | } |
307: | |
308: | private function getNameScope(): NameScope |
309: | { |
310: | return $this->nameScope; |
311: | } |
312: | |
313: | public function getNullableNameScope(): ?NameScope |
314: | { |
315: | return $this->nameScope; |
316: | } |
317: | |
318: | |
319: | |
320: | |
321: | public function getVarTags(): array |
322: | { |
323: | if ($this->varTags === false) { |
324: | $this->varTags = $this->phpDocNodeResolver->resolveVarTags( |
325: | $this->phpDocNode, |
326: | $this->getNameScope(), |
327: | ); |
328: | } |
329: | return $this->varTags; |
330: | } |
331: | |
332: | |
333: | |
334: | |
335: | public function getMethodTags(): array |
336: | { |
337: | if ($this->methodTags === false) { |
338: | $this->methodTags = $this->phpDocNodeResolver->resolveMethodTags( |
339: | $this->phpDocNode, |
340: | $this->getNameScope(), |
341: | ); |
342: | } |
343: | return $this->methodTags; |
344: | } |
345: | |
346: | |
347: | |
348: | |
349: | public function getPropertyTags(): array |
350: | { |
351: | if ($this->propertyTags === false) { |
352: | $this->propertyTags = $this->phpDocNodeResolver->resolvePropertyTags( |
353: | $this->phpDocNode, |
354: | $this->getNameScope(), |
355: | ); |
356: | } |
357: | return $this->propertyTags; |
358: | } |
359: | |
360: | |
361: | |
362: | |
363: | public function getTemplateTags(): array |
364: | { |
365: | return $this->templateTags; |
366: | } |
367: | |
368: | |
369: | |
370: | |
371: | public function getExtendsTags(): array |
372: | { |
373: | if ($this->extendsTags === false) { |
374: | $this->extendsTags = $this->phpDocNodeResolver->resolveExtendsTags( |
375: | $this->phpDocNode, |
376: | $this->getNameScope(), |
377: | ); |
378: | } |
379: | return $this->extendsTags; |
380: | } |
381: | |
382: | |
383: | |
384: | |
385: | public function getImplementsTags(): array |
386: | { |
387: | if ($this->implementsTags === false) { |
388: | $this->implementsTags = $this->phpDocNodeResolver->resolveImplementsTags( |
389: | $this->phpDocNode, |
390: | $this->getNameScope(), |
391: | ); |
392: | } |
393: | return $this->implementsTags; |
394: | } |
395: | |
396: | |
397: | |
398: | |
399: | public function getUsesTags(): array |
400: | { |
401: | if ($this->usesTags === false) { |
402: | $this->usesTags = $this->phpDocNodeResolver->resolveUsesTags( |
403: | $this->phpDocNode, |
404: | $this->getNameScope(), |
405: | ); |
406: | } |
407: | return $this->usesTags; |
408: | } |
409: | |
410: | |
411: | |
412: | |
413: | public function getParamTags(): array |
414: | { |
415: | if ($this->paramTags === false) { |
416: | $this->paramTags = $this->phpDocNodeResolver->resolveParamTags( |
417: | $this->phpDocNode, |
418: | $this->getNameScope(), |
419: | ); |
420: | } |
421: | return $this->paramTags; |
422: | } |
423: | |
424: | public function getReturnTag(): ?ReturnTag |
425: | { |
426: | if (is_bool($this->returnTag)) { |
427: | $this->returnTag = $this->phpDocNodeResolver->resolveReturnTag( |
428: | $this->phpDocNode, |
429: | $this->getNameScope(), |
430: | ); |
431: | } |
432: | return $this->returnTag; |
433: | } |
434: | |
435: | public function getThrowsTag(): ?ThrowsTag |
436: | { |
437: | if (is_bool($this->throwsTag)) { |
438: | $this->throwsTag = $this->phpDocNodeResolver->resolveThrowsTags( |
439: | $this->phpDocNode, |
440: | $this->getNameScope(), |
441: | ); |
442: | } |
443: | return $this->throwsTag; |
444: | } |
445: | |
446: | |
447: | |
448: | |
449: | public function getMixinTags(): array |
450: | { |
451: | if ($this->mixinTags === false) { |
452: | $this->mixinTags = $this->phpDocNodeResolver->resolveMixinTags( |
453: | $this->phpDocNode, |
454: | $this->getNameScope(), |
455: | ); |
456: | } |
457: | |
458: | return $this->mixinTags; |
459: | } |
460: | |
461: | |
462: | |
463: | |
464: | public function getTypeAliasTags(): array |
465: | { |
466: | if ($this->typeAliasTags === false) { |
467: | $this->typeAliasTags = $this->phpDocNodeResolver->resolveTypeAliasTags( |
468: | $this->phpDocNode, |
469: | $this->getNameScope(), |
470: | ); |
471: | } |
472: | |
473: | return $this->typeAliasTags; |
474: | } |
475: | |
476: | |
477: | |
478: | |
479: | public function getTypeAliasImportTags(): array |
480: | { |
481: | if ($this->typeAliasImportTags === false) { |
482: | $this->typeAliasImportTags = $this->phpDocNodeResolver->resolveTypeAliasImportTags( |
483: | $this->phpDocNode, |
484: | $this->getNameScope(), |
485: | ); |
486: | } |
487: | |
488: | return $this->typeAliasImportTags; |
489: | } |
490: | |
491: | public function getDeprecatedTag(): ?DeprecatedTag |
492: | { |
493: | if (is_bool($this->deprecatedTag)) { |
494: | $this->deprecatedTag = $this->phpDocNodeResolver->resolveDeprecatedTag( |
495: | $this->phpDocNode, |
496: | $this->getNameScope(), |
497: | ); |
498: | } |
499: | return $this->deprecatedTag; |
500: | } |
501: | |
502: | public function isDeprecated(): bool |
503: | { |
504: | if ($this->isDeprecated === null) { |
505: | $this->isDeprecated = $this->phpDocNodeResolver->resolveIsDeprecated( |
506: | $this->phpDocNode, |
507: | ); |
508: | } |
509: | return $this->isDeprecated; |
510: | } |
511: | |
512: | public function isInternal(): bool |
513: | { |
514: | if ($this->isInternal === null) { |
515: | $this->isInternal = $this->phpDocNodeResolver->resolveIsInternal( |
516: | $this->phpDocNode, |
517: | ); |
518: | } |
519: | return $this->isInternal; |
520: | } |
521: | |
522: | public function isFinal(): bool |
523: | { |
524: | if ($this->isFinal === null) { |
525: | $this->isFinal = $this->phpDocNodeResolver->resolveIsFinal( |
526: | $this->phpDocNode, |
527: | ); |
528: | } |
529: | return $this->isFinal; |
530: | } |
531: | |
532: | public function hasConsistentConstructor(): bool |
533: | { |
534: | if ($this->hasConsistentConstructor === null) { |
535: | $this->hasConsistentConstructor = $this->phpDocNodeResolver->resolveHasConsistentConstructor( |
536: | $this->phpDocNode, |
537: | ); |
538: | } |
539: | return $this->hasConsistentConstructor; |
540: | } |
541: | |
542: | public function acceptsNamedArguments(): bool |
543: | { |
544: | if ($this->acceptsNamedArguments === null) { |
545: | $this->acceptsNamedArguments = $this->phpDocNodeResolver->resolveAcceptsNamedArguments( |
546: | $this->phpDocNode, |
547: | ); |
548: | } |
549: | return $this->acceptsNamedArguments; |
550: | } |
551: | |
552: | public function getTemplateTypeMap(): TemplateTypeMap |
553: | { |
554: | return $this->templateTypeMap; |
555: | } |
556: | |
557: | public function isPure(): ?bool |
558: | { |
559: | if ($this->isPure === 'notLoaded') { |
560: | $pure = $this->phpDocNodeResolver->resolveIsPure( |
561: | $this->phpDocNode, |
562: | ); |
563: | if ($pure) { |
564: | $this->isPure = true; |
565: | return $this->isPure; |
566: | } else { |
567: | $impure = $this->phpDocNodeResolver->resolveIsImpure( |
568: | $this->phpDocNode, |
569: | ); |
570: | if ($impure) { |
571: | $this->isPure = false; |
572: | return $this->isPure; |
573: | } |
574: | } |
575: | |
576: | $this->isPure = null; |
577: | } |
578: | |
579: | return $this->isPure; |
580: | } |
581: | |
582: | public function isReadOnly(): bool |
583: | { |
584: | if ($this->isReadOnly === null) { |
585: | $this->isReadOnly = $this->phpDocNodeResolver->resolveIsReadOnly( |
586: | $this->phpDocNode, |
587: | ); |
588: | } |
589: | return $this->isReadOnly; |
590: | } |
591: | |
592: | public function isImmutable(): bool |
593: | { |
594: | if ($this->isImmutable === null) { |
595: | $this->isImmutable = $this->phpDocNodeResolver->resolveIsImmutable( |
596: | $this->phpDocNode, |
597: | ); |
598: | } |
599: | return $this->isImmutable; |
600: | } |
601: | |
602: | public function isAllowedPrivateMutation(): bool |
603: | { |
604: | if ($this->isAllowedPrivateMutation === null) { |
605: | $this->isAllowedPrivateMutation = $this->phpDocNodeResolver->resolveAllowPrivateMutation( |
606: | $this->phpDocNode, |
607: | ); |
608: | } |
609: | |
610: | return $this->isAllowedPrivateMutation; |
611: | } |
612: | |
613: | |
614: | |
615: | |
616: | |
617: | |
618: | |
619: | private static function mergeVarTags(array $varTags, array $parents, array $parentPhpDocBlocks): array |
620: | { |
621: | |
622: | if (count($varTags) > 0) { |
623: | return $varTags; |
624: | } |
625: | |
626: | foreach ($parents as $i => $parent) { |
627: | $result = self::mergeOneParentVarTags($parent, $parentPhpDocBlocks[$i]); |
628: | if ($result === null) { |
629: | continue; |
630: | } |
631: | |
632: | return $result; |
633: | } |
634: | |
635: | return []; |
636: | } |
637: | |
638: | |
639: | |
640: | |
641: | |
642: | private static function mergeOneParentVarTags(self $parent, PhpDocBlock $phpDocBlock): ?array |
643: | { |
644: | foreach ($parent->getVarTags() as $key => $parentVarTag) { |
645: | return [$key => self::resolveTemplateTypeInTag($parentVarTag, $phpDocBlock)]; |
646: | } |
647: | |
648: | return null; |
649: | } |
650: | |
651: | |
652: | |
653: | |
654: | |
655: | |
656: | |
657: | private static function mergeParamTags(array $paramTags, array $parents, array $parentPhpDocBlocks): array |
658: | { |
659: | foreach ($parents as $i => $parent) { |
660: | $paramTags = self::mergeOneParentParamTags($paramTags, $parent, $parentPhpDocBlocks[$i]); |
661: | } |
662: | |
663: | return $paramTags; |
664: | } |
665: | |
666: | |
667: | |
668: | |
669: | |
670: | |
671: | private static function mergeOneParentParamTags(array $paramTags, self $parent, PhpDocBlock $phpDocBlock): array |
672: | { |
673: | $parentParamTags = $phpDocBlock->transformArrayKeysWithParameterNameMapping($parent->getParamTags()); |
674: | |
675: | foreach ($parentParamTags as $name => $parentParamTag) { |
676: | if (array_key_exists($name, $paramTags)) { |
677: | continue; |
678: | } |
679: | |
680: | $paramTags[$name] = self::resolveTemplateTypeInTag($parentParamTag, $phpDocBlock); |
681: | } |
682: | |
683: | return $paramTags; |
684: | } |
685: | |
686: | |
687: | |
688: | |
689: | |
690: | |
691: | private static function mergeReturnTags(?ReturnTag $returnTag, array $parents, array $parentPhpDocBlocks): ?ReturnTag |
692: | { |
693: | if ($returnTag !== null) { |
694: | return $returnTag; |
695: | } |
696: | |
697: | foreach ($parents as $i => $parent) { |
698: | $result = self::mergeOneParentReturnTag($returnTag, $parent, $parentPhpDocBlocks[$i]); |
699: | if ($result === null) { |
700: | continue; |
701: | } |
702: | |
703: | return $result; |
704: | } |
705: | |
706: | return null; |
707: | } |
708: | |
709: | private static function mergeOneParentReturnTag(?ReturnTag $returnTag, self $parent, PhpDocBlock $phpDocBlock): ?ReturnTag |
710: | { |
711: | $parentReturnTag = $parent->getReturnTag(); |
712: | if ($parentReturnTag === null) { |
713: | return $returnTag; |
714: | } |
715: | |
716: | $parentType = $parentReturnTag->getType(); |
717: | |
718: | |
719: | |
720: | if ($returnTag !== null && $parentType->isSuperTypeOf($returnTag->getType())->yes()) { |
721: | return null; |
722: | } |
723: | |
724: | return self::resolveTemplateTypeInTag( |
725: | $parentReturnTag->withType( |
726: | $phpDocBlock->transformConditionalReturnTypeWithParameterNameMapping($parentReturnTag->getType()), |
727: | )->toImplicit(), |
728: | $phpDocBlock, |
729: | ); |
730: | } |
731: | |
732: | |
733: | |
734: | |
735: | private static function mergeDeprecatedTags(?DeprecatedTag $deprecatedTag, array $parents): ?DeprecatedTag |
736: | { |
737: | if ($deprecatedTag !== null) { |
738: | return $deprecatedTag; |
739: | } |
740: | foreach ($parents as $parent) { |
741: | $result = $parent->getDeprecatedTag(); |
742: | if ($result === null) { |
743: | continue; |
744: | } |
745: | return $result; |
746: | } |
747: | |
748: | return null; |
749: | } |
750: | |
751: | |
752: | |
753: | |
754: | private static function mergeThrowsTags(?ThrowsTag $throwsTag, array $parents): ?ThrowsTag |
755: | { |
756: | if ($throwsTag !== null) { |
757: | return $throwsTag; |
758: | } |
759: | foreach ($parents as $parent) { |
760: | $result = $parent->getThrowsTag(); |
761: | if ($result === null) { |
762: | continue; |
763: | } |
764: | |
765: | return $result; |
766: | } |
767: | |
768: | return null; |
769: | } |
770: | |
771: | |
772: | |
773: | |
774: | |
775: | |
776: | private static function resolveTemplateTypeInTag(TypedTag $tag, PhpDocBlock $phpDocBlock): TypedTag |
777: | { |
778: | $type = TemplateTypeHelper::resolveTemplateTypes( |
779: | $tag->getType(), |
780: | $phpDocBlock->getClassReflection()->getActiveTemplateTypeMap(), |
781: | ); |
782: | return $tag->withType($type); |
783: | } |
784: | |
785: | } |
786: | |