3333use PHPStan \Type \ArrayType ;
3434use PHPStan \Type \Constant \ConstantArrayType ;
3535use PHPStan \Type \ErrorType ;
36+ use PHPStan \Type \FileTypeMapper ;
3637use PHPStan \Type \Generic \TemplateTypeHelper ;
3738use PHPStan \Type \Generic \TemplateTypeMap ;
3839use PHPStan \Type \MixedType ;
@@ -69,6 +70,8 @@ class PhpClassReflectionExtension
6970
7071 private \PHPStan \Reflection \ReflectionProvider $ reflectionProvider ;
7172
73+ private FileTypeMapper $ fileTypeMapper ;
74+
7275 /** @var string[] */
7376 private array $ universalObjectCratesClasses ;
7477
@@ -101,6 +104,7 @@ class PhpClassReflectionExtension
101104 * @param \PHPStan\Parser\Parser $parser
102105 * @param \PHPStan\PhpDoc\StubPhpDocProvider $stubPhpDocProvider
103106 * @param \PHPStan\Reflection\ReflectionProvider $reflectionProvider
107+ * @param FileTypeMapper $fileTypeMapper
104108 * @param bool $inferPrivatePropertyTypeFromConstructor
105109 * @param string[] $universalObjectCratesClasses
106110 */
@@ -115,6 +119,7 @@ public function __construct(
115119 Parser $ parser ,
116120 StubPhpDocProvider $ stubPhpDocProvider ,
117121 ReflectionProvider $ reflectionProvider ,
122+ FileTypeMapper $ fileTypeMapper ,
118123 bool $ inferPrivatePropertyTypeFromConstructor ,
119124 array $ universalObjectCratesClasses
120125 )
@@ -129,6 +134,7 @@ public function __construct(
129134 $ this ->parser = $ parser ;
130135 $ this ->stubPhpDocProvider = $ stubPhpDocProvider ;
131136 $ this ->reflectionProvider = $ reflectionProvider ;
137+ $ this ->fileTypeMapper = $ fileTypeMapper ;
132138 $ this ->inferPrivatePropertyTypeFromConstructor = $ inferPrivatePropertyTypeFromConstructor ;
133139 $ this ->universalObjectCratesClasses = $ universalObjectCratesClasses ;
134140 }
@@ -422,9 +428,11 @@ private function createMethod(
422428 }
423429 foreach ($ variantNumbers as $ variantNumber ) {
424430 $ methodSignature = $ this ->signatureMapProvider ->getMethodSignature ($ declaringClassName , $ methodReflection ->getName (), $ reflectionMethod , $ variantNumber );
425- $ phpDocReturnType = null ;
431+ $ stubPhpDocReturnType = null ;
426432 $ stubPhpDocParameterTypes = [];
427433 $ stubPhpDocParameterVariadicity = [];
434+ $ phpDocParameterTypes = [];
435+ $ phpDocReturnType = null ;
428436 if (count ($ variantNumbers ) === 1 ) {
429437 $ stubPhpDocPair = $ this ->findMethodPhpDocIncludingAncestors ($ declaringClass , $ methodReflection ->getName (), array_map (static function (ParameterSignature $ parameterSignature ): string {
430438 return $ parameterSignature ->getName ();
@@ -435,9 +443,8 @@ private function createMethod(
435443 $ templateTypeMap = $ stubDeclaringClass ->getActiveTemplateTypeMap ();
436444 $ returnTag = $ stubPhpDoc ->getReturnTag ();
437445 if ($ returnTag !== null ) {
438- $ stubPhpDocReturnType = $ returnTag ->getType ();
439- $ phpDocReturnType = TemplateTypeHelper::resolveTemplateTypes (
440- $ stubPhpDocReturnType ,
446+ $ stubPhpDocReturnType = TemplateTypeHelper::resolveTemplateTypes (
447+ $ returnTag ->getType (),
441448 $ templateTypeMap
442449 );
443450 }
@@ -449,9 +456,27 @@ private function createMethod(
449456 );
450457 $ stubPhpDocParameterVariadicity [$ name ] = $ paramTag ->isVariadic ();
451458 }
459+ } elseif ($ reflectionMethod !== null && $ reflectionMethod ->getDocComment () !== false ) {
460+ $ filename = $ reflectionMethod ->getFileName ();
461+ if ($ filename !== false ) {
462+ $ phpDocBlock = $ this ->fileTypeMapper ->getResolvedPhpDoc (
463+ $ filename ,
464+ $ declaringClassName ,
465+ null ,
466+ $ reflectionMethod ->getName (),
467+ $ reflectionMethod ->getDocComment ()
468+ );
469+ $ returnTag = $ phpDocBlock ->getReturnTag ();
470+ if ($ returnTag !== null ) {
471+ $ phpDocReturnType = $ returnTag ->getType ();
472+ }
473+ foreach ($ phpDocBlock ->getParamTags () as $ name => $ paramTag ) {
474+ $ phpDocParameterTypes [$ name ] = $ paramTag ->getType ();
475+ }
476+ }
452477 }
453478 }
454- $ variants [] = $ this ->createNativeMethodVariant ($ methodSignature , $ stubPhpDocParameterTypes , $ stubPhpDocParameterVariadicity , $ phpDocReturnType );
479+ $ variants [] = $ this ->createNativeMethodVariant ($ methodSignature , $ stubPhpDocParameterTypes , $ stubPhpDocParameterVariadicity , $ stubPhpDocReturnType , $ phpDocParameterTypes , $ phpDocReturnType );
455480 }
456481
457482 if ($ this ->signatureMapProvider ->hasMethodMetadata ($ declaringClassName , $ methodReflection ->getName ())) {
@@ -557,36 +582,65 @@ private function createMethod(
557582 * @param FunctionSignature $methodSignature
558583 * @param array<string, Type> $stubPhpDocParameterTypes
559584 * @param array<string, bool> $stubPhpDocParameterVariadicity
585+ * @param Type|null $stubPhpDocReturnType
586+ * @param array<string, Type> $phpDocParameterTypes
560587 * @param Type|null $phpDocReturnType
561588 * @return FunctionVariantWithPhpDocs
562589 */
563590 private function createNativeMethodVariant (
564591 FunctionSignature $ methodSignature ,
565592 array $ stubPhpDocParameterTypes ,
566593 array $ stubPhpDocParameterVariadicity ,
594+ ?Type $ stubPhpDocReturnType ,
595+ array $ phpDocParameterTypes ,
567596 ?Type $ phpDocReturnType
568597 ): FunctionVariantWithPhpDocs
569598 {
570599 $ parameters = [];
571600 foreach ($ methodSignature ->getParameters () as $ parameterSignature ) {
601+ $ type = null ;
602+ $ phpDocType = null ;
603+
604+ if (isset ($ stubPhpDocParameterTypes [$ parameterSignature ->getName ()])) {
605+ $ type = $ stubPhpDocParameterTypes [$ parameterSignature ->getName ()];
606+ $ phpDocType = $ stubPhpDocParameterTypes [$ parameterSignature ->getName ()];
607+ } elseif (isset ($ phpDocParameterTypes [$ parameterSignature ->getName ()])) {
608+ $ type = TypehintHelper::decideType (
609+ $ parameterSignature ->getNativeType (),
610+ $ phpDocParameterTypes [$ parameterSignature ->getName ()]
611+ );
612+ $ phpDocType = $ phpDocParameterTypes [$ parameterSignature ->getName ()];
613+ }
614+
572615 $ parameters [] = new NativeParameterWithPhpDocsReflection (
573616 $ parameterSignature ->getName (),
574617 $ parameterSignature ->isOptional (),
575- $ stubPhpDocParameterTypes [ $ parameterSignature -> getName ()] ?? $ parameterSignature ->getType (),
576- $ stubPhpDocParameterTypes [ $ parameterSignature -> getName ()] ?? new MixedType (),
618+ $ type ?? $ parameterSignature ->getType (),
619+ $ phpDocType ?? new MixedType (),
577620 $ parameterSignature ->getNativeType (),
578621 $ parameterSignature ->passedByReference (),
579622 $ stubPhpDocParameterVariadicity [$ parameterSignature ->getName ()] ?? $ parameterSignature ->isVariadic (),
580623 null
581624 );
582625 }
583626
627+ $ returnType = null ;
628+ if ($ stubPhpDocReturnType !== null ) {
629+ $ returnType = $ stubPhpDocReturnType ;
630+ $ phpDocReturnType = $ stubPhpDocReturnType ;
631+ } elseif ($ phpDocReturnType !== null ) {
632+ $ returnType = TypehintHelper::decideType (
633+ $ methodSignature ->getReturnType (),
634+ $ phpDocReturnType
635+ );
636+ }
637+
584638 return new FunctionVariantWithPhpDocs (
585639 TemplateTypeMap::createEmpty (),
586640 null ,
587641 $ parameters ,
588642 $ methodSignature ->isVariadic (),
589- $ phpDocReturnType ?? $ methodSignature ->getReturnType (),
643+ $ returnType ?? $ methodSignature ->getReturnType (),
590644 $ phpDocReturnType ?? new MixedType (),
591645 $ methodSignature ->getNativeReturnType ()
592646 );
0 commit comments