44
55namespace Rector \DeadCode \Rector \FunctionLike ;
66
7+ use PHPStan \Type \UnionType as PHPStanUnionType ;
78use PhpParser \Node ;
89use PhpParser \Node \ComplexType ;
910use PhpParser \Node \Expr ;
2324use PHPStan \Type \NullType ;
2425use PHPStan \Type \Type ;
2526use PHPStan \Type \TypeCombinator ;
27+ use Rector \BetterPhpDocParser \PhpDocInfo \PhpDocInfo ;
28+ use Rector \BetterPhpDocParser \PhpDocInfo \PhpDocInfoFactory ;
29+ use Rector \BetterPhpDocParser \PhpDocManipulator \PhpDocTypeChanger ;
2630use Rector \PhpParser \Node \BetterNodeFinder ;
2731use Rector \PHPStan \ScopeFetcher ;
2832use Rector \PHPStanStaticTypeMapper \Enum \TypeKind ;
@@ -46,6 +50,8 @@ public function __construct(
4650 private readonly StaticTypeMapper $ staticTypeMapper ,
4751 private readonly ReflectionResolver $ reflectionResolver ,
4852 private readonly SilentVoidResolver $ silentVoidResolver ,
53+ private readonly PhpDocTypeChanger $ phpDocTypeChanger ,
54+ private readonly PhpDocInfoFactory $ phpDocInfoFactory ,
4955 ) {
5056 }
5157
@@ -120,17 +126,31 @@ public function refactor(Node $node): ?Node
120126 return null ;
121127 }
122128
123- $ returnType = $ node ->returnType ;
124- Assert::isInstanceOfAny ($ returnType , [UnionType::class, NullableType::class]);
129+ $ phpDocInfo = $ this ->phpDocInfoFactory ->createFromNode ($ node );
130+
131+ if ($ phpDocInfo ?->hasByName('@return ' )) {
132+ $ returnType = $ phpDocInfo ->getReturnType ();
133+ } else {
134+ $ returnType = $ node ->returnType ;
135+ Assert::isInstanceOfAny ($ returnType , [UnionType::class, NullableType::class]);
136+ $ returnType = $ this ->staticTypeMapper ->mapPhpParserNodePHPStanType ($ returnType );
137+ }
125138
126139 $ actualReturnTypes = $ this ->collectActualReturnTypes ($ node , $ returnStatements , $ isAlwaysTerminating );
127- $ newReturnType = $ this ->narrowUnionReturnType ($ returnType , $ actualReturnTypes );
140+ $ newReturnType = $ this ->narrowReturnType ($ returnType , $ actualReturnTypes );
128141
129142 if ($ newReturnType === null ) {
130143 return null ;
131144 }
132145
133- $ node ->returnType = $ newReturnType ;
146+ $ node ->returnType = $ this ->staticTypeMapper ->mapPHPStanTypeToPhpParserNode (
147+ $ newReturnType ,
148+ TypeKind::RETURN
149+ );
150+
151+ if ($ phpDocInfo instanceof PhpDocInfo) {
152+ $ this ->phpDocTypeChanger ->changeReturnType ($ node , $ phpDocInfo , $ newReturnType );
153+ }
134154
135155 return $ node ;
136156 }
@@ -182,7 +202,7 @@ private function collectActualReturnTypes(
182202 bool $ isAlwaysTerminating ,
183203 ): array {
184204 if ($ node instanceof ArrowFunction) {
185- return [$ this ->nodeTypeResolver -> getNativeType ($ node ->expr )];
205+ return [$ this ->getType ($ node ->expr )];
186206 }
187207
188208 $ returnTypes = [];
@@ -192,7 +212,7 @@ private function collectActualReturnTypes(
192212 continue ;
193213 }
194214
195- $ returnTypes [] = $ this ->nodeTypeResolver -> getNativeType ($ returnStatement ->expr );
215+ $ returnTypes [] = $ this ->getType ($ returnStatement ->expr );
196216 }
197217
198218 if (! $ isAlwaysTerminating ) {
@@ -205,23 +225,15 @@ private function collectActualReturnTypes(
205225 /**
206226 * @param Type[] $actualReturnTypes
207227 */
208- private function narrowUnionReturnType (
209- UnionType |NullableType $ returnType ,
210- array $ actualReturnTypes
211- ): ComplexType |Identifier |Name |null {
212- $ types = $ returnType instanceof UnionType
213- ? $ returnType ->types
214- : [$ returnType ->type , new Identifier ('null ' )];
228+ private function narrowReturnType (Type $ returnType , array $ actualReturnTypes ): Type |null
229+ {
230+ $ types = $ returnType instanceof PHPStanUnionType ? $ returnType ->getTypes () : [$ returnType ];
215231 $ usedTypes = [];
216232
217233 foreach ($ types as $ type ) {
218- $ declaredType = $ type instanceof Expr
219- ? $ this ->nodeTypeResolver ->getNativeType ($ type )
220- : $ this ->getType ($ type );
221-
222234 foreach ($ actualReturnTypes as $ actualType ) {
223- if (! $ declaredType ->isSuperTypeOf ($ actualType )->no ()) {
224- $ usedTypes [] = $ declaredType ;
235+ if (! $ type ->isSuperTypeOf ($ actualType )->no ()) {
236+ $ usedTypes [] = $ type ;
225237 break ;
226238 }
227239 }
@@ -233,10 +245,7 @@ private function narrowUnionReturnType(
233245 return null ;
234246 }
235247
236- return $ this ->staticTypeMapper ->mapPHPStanTypeToPhpParserNode (
237- TypeCombinator::union (...$ usedTypes ),
238- TypeKind::RETURN
239- );
248+ return TypeCombinator::union (...$ usedTypes );
240249 }
241250
242251 private function hasYield (ClassMethod |Function_ |Closure |ArrowFunction $ node ): bool
0 commit comments