2323use Rector \BetterPhpDocParser \PhpDocInfo \PhpDocInfoFactory ;
2424use Rector \BetterPhpDocParser \PhpDocManipulator \PhpDocTypeChanger ;
2525use Rector \Rector \AbstractRector ;
26+ use Rector \StaticTypeMapper \StaticTypeMapper ;
2627use Rector \StaticTypeMapper \ValueObject \Type \NonExistingObjectType ;
2728use Rector \TypeDeclarationDocblocks \NodeFinder \ReturnNodeFinder ;
29+ use Rector \TypeDeclarationDocblocks \TagNodeAnalyzer \UsefulArrayTagNodeAnalyzer ;
2830use Symplify \RuleDocGenerator \ValueObject \CodeSample \CodeSample ;
2931use Symplify \RuleDocGenerator \ValueObject \RuleDefinition ;
3032
@@ -37,6 +39,8 @@ public function __construct(
3739 private readonly PhpDocInfoFactory $ phpDocInfoFactory ,
3840 private readonly ReturnNodeFinder $ returnNodeFinder ,
3941 private readonly PhpDocTypeChanger $ phpDocTypeChanger ,
42+ private readonly StaticTypeMapper $ staticTypeMapper ,
43+ private readonly UsefulArrayTagNodeAnalyzer $ usefulArrayTagNodeAnalyzer
4044 ) {
4145 }
4246
@@ -101,7 +105,11 @@ public function refactor(Node $node): ?Node
101105 $ phpDocInfo = $ this ->phpDocInfoFactory ->createFromNodeOrEmpty ($ node );
102106 $ returnType = $ phpDocInfo ->getReturnType ();
103107
104- if (! $ returnType instanceof MixedType || $ returnType ->isExplicitMixed ()) {
108+ if ($ returnType instanceof ArrayType && ! $ returnType ->getItemType () instanceof MixedType) {
109+ return null ;
110+ }
111+
112+ if ($ this ->usefulArrayTagNodeAnalyzer ->isUsefulArrayTag ($ phpDocInfo ->getReturnTagValue ())) {
105113 return null ;
106114 }
107115
@@ -124,10 +132,49 @@ public function refactor(Node $node): ?Node
124132 return null ;
125133 }
126134
135+ if ($ this ->isVariableExclusivelyArrayDimAssigned ($ node , $ returnedVariableName ) === false ) {
136+ return null ;
137+ }
138+
139+ $ arrayObjectType = $ this ->matchArrayObjectType ($ returnedType );
140+ if (! $ arrayObjectType instanceof ObjectType) {
141+ return null ;
142+ }
143+
144+ $ objectTypeArrayType = new ArrayType (new MixedType (), $ arrayObjectType );
145+ $ returnTypeNode = $ this ->staticTypeMapper ->mapPHPStanTypeToPHPStanPhpDocTypeNode ($ objectTypeArrayType );
146+ $ this ->phpDocTypeChanger ->changeReturnTypeNode ($ node , $ phpDocInfo , $ returnTypeNode );
147+
148+ return $ node ;
149+ }
150+
151+ private function matchArrayObjectType (Type $ returnedType ): ?Type
152+ {
153+ if ($ returnedType instanceof IntersectionType) {
154+ foreach ($ returnedType ->getTypes () as $ intersectionedType ) {
155+ if ($ intersectionedType instanceof AccessoryArrayListType) {
156+ continue ;
157+ }
158+
159+ if ($ intersectionedType instanceof ArrayType && $ intersectionedType ->getItemType () instanceof ObjectType) {
160+ return $ intersectionedType ->getItemType ();
161+ }
162+
163+ return null ;
164+ }
165+ }
166+
167+ return null ;
168+ }
169+
170+ private function isVariableExclusivelyArrayDimAssigned (
171+ ClassMethod |Function_ $ functionLike ,
172+ string $ variableName
173+ ): bool {
127174 $ isVariableExclusivelyArrayDimAssigned = true ;
128175
129- $ this ->traverseNodesWithCallable ((array ) $ node ->stmts , function ($ node ) use (
130- $ returnedVariableName ,
176+ $ this ->traverseNodesWithCallable ((array ) $ functionLike ->stmts , function ($ node ) use (
177+ $ variableName ,
131178 &$ isVariableExclusivelyArrayDimAssigned
132179 ): ?int {
133180 if ($ node instanceof Assign) {
@@ -139,7 +186,7 @@ public function refactor(Node $node): ?Node
139186 return null ;
140187 }
141188
142- if ($ this ->isName ($ arrayDimFetch ->var , $ returnedVariableName )) {
189+ if ($ this ->isName ($ arrayDimFetch ->var , $ variableName )) {
143190 if ($ arrayDimFetch ->dim instanceof Expr) {
144191 $ isVariableExclusivelyArrayDimAssigned = false ;
145192 }
@@ -160,7 +207,7 @@ public function refactor(Node $node): ?Node
160207
161208 if ($ node ->var instanceof Variable && $ this ->isName (
162209 $ node ->var ,
163- $ returnedVariableName
210+ $ variableName
164211 ) && $ node ->expr instanceof Array_) {
165212 if ($ node ->expr ->items === []) {
166213 // ignore empty array assignment
@@ -172,57 +219,21 @@ public function refactor(Node $node): ?Node
172219 }
173220
174221 if ($ node instanceof Return_ && $ node ->expr instanceof Variable) {
175- if ($ this ->isName ($ node ->expr , $ returnedVariableName )) {
222+ if ($ this ->isName ($ node ->expr , $ variableName )) {
176223 // ignore lower value
177224 return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN ;
178225 }
179226
180227 $ isVariableExclusivelyArrayDimAssigned = false ;
181228 }
182229
183- if ($ node instanceof Variable && $ this ->isName ($ node , $ returnedVariableName )) {
230+ if ($ node instanceof Variable && $ this ->isName ($ node , $ variableName )) {
184231 $ isVariableExclusivelyArrayDimAssigned = false ;
185232 }
186233
187234 return null ;
188235 });
189236
190- if ($ isVariableExclusivelyArrayDimAssigned === false ) {
191- return null ;
192- }
193-
194- $ arrayObjectType = $ this ->matchArrayObjectType ($ returnedType );
195- if (! $ arrayObjectType instanceof ObjectType) {
196- return null ;
197- }
198-
199- //$arrayReturnDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($arrayObjectType);
200-
201- $ objectTypeArrayType = new ArrayType (new MixedType (), $ arrayObjectType );
202- $ hasChanged = $ this ->phpDocTypeChanger ->changeReturnType ($ node , $ phpDocInfo , $ objectTypeArrayType );
203- if (! $ hasChanged ) {
204- return null ;
205- }
206-
207- return $ node ;
208- }
209-
210- private function matchArrayObjectType (Type $ returnedType ): ?Type
211- {
212- if ($ returnedType instanceof IntersectionType) {
213- foreach ($ returnedType ->getTypes () as $ intersectionedType ) {
214- if ($ intersectionedType instanceof AccessoryArrayListType) {
215- continue ;
216- }
217-
218- if ($ intersectionedType instanceof ArrayType && $ intersectionedType ->getItemType () instanceof ObjectType) {
219- return $ intersectionedType ->getItemType ();
220- }
221-
222- return null ;
223- }
224- }
225-
226- return null ;
237+ return $ isVariableExclusivelyArrayDimAssigned ;
227238 }
228239}
0 commit comments