66use PHPStan \Analyser \Scope ;
77use PHPStan \DependencyInjection \AutowiredParameter ;
88use PHPStan \DependencyInjection \AutowiredService ;
9+ use PHPStan \Reflection \ParametersAcceptor ;
910use PHPStan \Reflection \ReflectionProvider ;
1011use PHPStan \Type \BenevolentUnionType ;
1112use PHPStan \Type \CallableType ;
1213use PHPStan \Type \ClosureType ;
1314use PHPStan \Type \ErrorType ;
15+ use PHPStan \Type \Generic \GenericObjectType ;
1416use PHPStan \Type \Generic \TemplateMixedType ;
1517use PHPStan \Type \IntersectionType ;
1618use PHPStan \Type \MixedType ;
@@ -49,6 +51,29 @@ public function __construct(
4951 {
5052 }
5153
54+ /**
55+ * @param callable(Type): Type $traverse
56+ */
57+ private function traverseWithoutMapping (Type $ type , ?Type $ acceptingType , callable $ traverse ): Type
58+ {
59+ if ($ type instanceof GenericObjectType && $ acceptingType !== null ) {
60+ return $ type ->traverseSimultaneously ($ acceptingType , function (Type $ type , Type $ acceptingType ) use ($ traverse ): Type {
61+ if (
62+ !$ this ->checkNullables
63+ && !$ type instanceof BenevolentUnionType
64+ && TypeCombinator::containsNull ($ type )
65+ && !TypeCombinator::containsNull ($ acceptingType )
66+ ) {
67+ return $ traverse (TypeCombinator::removeNull ($ type ));
68+ }
69+
70+ return $ type ;
71+ });
72+ }
73+
74+ return $ traverse ($ type );
75+ }
76+
5277 /** @api */
5378 public function isThis (Expr $ expression ): bool
5479 {
@@ -93,9 +118,10 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType):
93118 return $ acceptedType ;
94119 }
95120
121+ $ acceptingReturnType = $ acceptingType instanceof ParametersAcceptor ? $ acceptingType ->getReturnType () : null ;
96122 return new CallableType (
97123 $ acceptedType ->getParameters (),
98- $ traverse ($ acceptedType ->getReturnType ()),
124+ $ this -> traverseWithoutMapping ($ acceptedType ->getReturnType (), $ acceptingReturnType , $ traverse ),
99125 $ acceptedType ->isVariadic (),
100126 $ acceptedType ->getTemplateTypeMap (),
101127 $ acceptedType ->getResolvedTemplateTypeMap (),
@@ -109,9 +135,10 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType):
109135 return $ acceptedType ;
110136 }
111137
138+ $ acceptingReturnType = $ acceptingType instanceof ParametersAcceptor ? $ acceptingType ->getReturnType () : null ;
112139 return new ClosureType (
113140 $ acceptedType ->getParameters (),
114- $ traverse ($ acceptedType ->getReturnType ()),
141+ $ this -> traverseWithoutMapping ($ acceptedType ->getReturnType (), $ acceptingReturnType , $ traverse ),
115142 $ acceptedType ->isVariadic (),
116143 $ acceptedType ->getTemplateTypeMap (),
117144 $ acceptedType ->getResolvedTemplateTypeMap (),
@@ -127,6 +154,10 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType):
127154 );
128155 }
129156
157+ if ($ acceptedType instanceof GenericObjectType) {
158+ return $ this ->traverseWithoutMapping ($ acceptedType , $ acceptingType , $ traverse );
159+ }
160+
130161 if (
131162 !$ this ->checkNullables
132163 && !$ acceptingType instanceof NullType
0 commit comments