@@ -154,6 +154,10 @@ private function getMethodReturnTypeForHydrationMode(
154154 return $ this ->originalReturnType ($ methodReflection );
155155 }
156156
157+ if (null === $ queryResultType ) {
158+ return $ this ->originalReturnType ($ methodReflection );
159+ }
160+
157161 switch ($ methodReflection ->getName ()) {
158162 case 'getSingleResult ' :
159163 return $ queryResultType ;
@@ -187,13 +191,21 @@ private function getMethodReturnTypeForHydrationMode(
187191 }
188192 }
189193
190- private function getArrayHydratedReturnType (Type $ queryResultType ): Type
194+ /**
195+ * When we're array-hydrating object, we're not sure of the shape of the array.
196+ * We could return `new ArrayTyp(new MixedType(), new MixedType())`
197+ * but the lack of precision in the array keys/values would give false positive.
198+ *
199+ * @see https://github.com/phpstan/phpstan-doctrine/pull/412#issuecomment-1497092934
200+ */
201+ private function getArrayHydratedReturnType (Type $ queryResultType ): ?Type
191202 {
192203 $ objectManager = $ this ->objectMetadataResolver ->getObjectManager ();
193204
194- return TypeTraverser::map (
205+ $ mixedFound = false ;
206+ $ queryResultType = TypeTraverser::map (
195207 $ queryResultType ,
196- static function (Type $ type , callable $ traverse ) use ($ objectManager ): Type {
208+ static function (Type $ type , callable $ traverse ) use ($ objectManager, & $ mixedFound ): Type {
197209 $ isObject = (new ObjectWithoutClassType ())->isSuperTypeOf ($ type );
198210 if ($ isObject ->no ()) {
199211 return $ traverse ($ type );
@@ -203,25 +215,35 @@ static function (Type $type, callable $traverse) use ($objectManager): Type {
203215 || !$ type instanceof TypeWithClassName
204216 || $ objectManager === null
205217 ) {
218+ $ mixedFound = true ;
219+
206220 return new MixedType ();
207221 }
208222
209223 if (!$ objectManager ->getMetadataFactory ()->hasMetadataFor ($ type ->getClassName ())) {
210224 return $ traverse ($ type );
211225 }
212226
213- // We could return `new ArrayTyp(new MixedType(), new MixedType())`
214- // but the lack of precision in the array keys/values would give false positive
215- // @see https://github.com/phpstan/phpstan-doctrine/pull/412#issuecomment-1497092934
227+ $ mixedFound = true ;
228+
216229 return new MixedType ();
217230 }
218231 );
232+
233+ return $ mixedFound ? null : $ queryResultType ;
219234 }
220235
221- private function getScalarHydratedReturnType (Type $ queryResultType ): Type
236+ /**
237+ * When we're scalar-hydrating object, we're not sure of the shape of the array.
238+ * We could return `new ArrayTyp(new MixedType(), new MixedType())`
239+ * but the lack of precision in the array keys/values would give false positive.
240+ *
241+ * @see https://github.com/phpstan/phpstan-doctrine/pull/453#issuecomment-1895415544
242+ */
243+ private function getScalarHydratedReturnType (Type $ queryResultType ): ?Type
222244 {
223245 if (!$ queryResultType ->isArray ()->yes ()) {
224- return new ArrayType ( new MixedType (), new MixedType ()) ;
246+ return null ;
225247 }
226248
227249 foreach ($ queryResultType ->getArrays () as $ arrayType ) {
@@ -231,34 +253,37 @@ private function getScalarHydratedReturnType(Type $queryResultType): Type
231253 !(new ObjectWithoutClassType ())->isSuperTypeOf ($ itemType )->no ()
232254 || !$ itemType ->isArray ()->no ()
233255 ) {
234- return new ArrayType (new MixedType (), new MixedType ());
256+ // We could return `new ArrayTyp(new MixedType(), new MixedType())`
257+ // but the lack of precision in the array keys/values would give false positive
258+ // @see https://github.com/phpstan/phpstan-doctrine/pull/453#issuecomment-1895415544
259+ return null ;
235260 }
236261 }
237262
238263 return $ queryResultType ;
239264 }
240265
241- private function getSimpleObjectHydratedReturnType (Type $ queryResultType ): Type
266+ private function getSimpleObjectHydratedReturnType (Type $ queryResultType ): ? Type
242267 {
243268 if ((new ObjectWithoutClassType ())->isSuperTypeOf ($ queryResultType )->yes ()) {
244269 return $ queryResultType ;
245270 }
246271
247- return new MixedType () ;
272+ return null ;
248273 }
249274
250- private function getSingleScalarHydratedReturnType (Type $ queryResultType ): Type
275+ private function getSingleScalarHydratedReturnType (Type $ queryResultType ): ? Type
251276 {
252277 $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
253- if (!$ queryResultType ->isConstantArray ()->yes ()) {
254- return new MixedType () ;
278+ if (null === $ queryResultType || !$ queryResultType ->isConstantArray ()->yes ()) {
279+ return null ;
255280 }
256281
257282 $ types = [];
258283 foreach ($ queryResultType ->getConstantArrays () as $ constantArrayType ) {
259284 $ values = $ constantArrayType ->getValueTypes ();
260285 if (count ($ values ) !== 1 ) {
261- return new MixedType () ;
286+ return null ;
262287 }
263288
264289 $ types [] = $ constantArrayType ->getFirstIterableValueType ();
@@ -267,18 +292,18 @@ private function getSingleScalarHydratedReturnType(Type $queryResultType): Type
267292 return TypeCombinator::union (...$ types );
268293 }
269294
270- private function getScalarColumnHydratedReturnType (Type $ queryResultType ): Type
295+ private function getScalarColumnHydratedReturnType (Type $ queryResultType ): ? Type
271296 {
272297 $ queryResultType = $ this ->getScalarHydratedReturnType ($ queryResultType );
273- if (!$ queryResultType ->isConstantArray ()->yes ()) {
274- return new MixedType () ;
298+ if (null === $ queryResultType || !$ queryResultType ->isConstantArray ()->yes ()) {
299+ return null ;
275300 }
276301
277302 $ types = [];
278303 foreach ($ queryResultType ->getConstantArrays () as $ constantArrayType ) {
279304 $ values = $ constantArrayType ->getValueTypes ();
280305 if (count ($ values ) !== 1 ) {
281- return new MixedType () ;
306+ return null ;
282307 }
283308
284309 $ types [] = $ constantArrayType ->getFirstIterableValueType ();
0 commit comments