44namespace Rector \TypeDeclaration \Rector \Class_ ;
55
66use PhpParser \Node ;
7- use PhpParser \Node \Name \FullyQualified ;
87use PhpParser \Node \Stmt \Class_ ;
98use PhpParser \Node \Stmt \ClassMethod ;
10- use PHPStan \Type \ IntersectionType ;
9+ use PHPStan \Reflection \ ClassReflection ;
1110use PHPStan \Type \ObjectType ;
1211use PHPStan \Type \Type ;
12+ use Rector \PHPStanStaticTypeMapper \Enum \TypeKind ;
1313use Rector \Rector \AbstractRector ;
14- use Rector \TypeDeclaration \TypeInferer \PropertyTypeInferer \TrustedClassMethodPropertyTypeInferer ;
14+ use Rector \Reflection \ReflectionResolver ;
15+ use Rector \StaticTypeMapper \StaticTypeMapper ;
16+ use Rector \TypeDeclaration \TypeInferer \PropertyTypeInferer \AllAssignNodePropertyTypeInferer ;
1517use Rector \ValueObject \MethodName ;
1618use Rector \ValueObject \PhpVersionFeature ;
1719use Rector \VersionBonding \Contract \MinPhpVersionInterface ;
@@ -24,9 +26,19 @@ final class TypedPropertyFromCreateMockAssignRector extends AbstractRector imple
2426{
2527 /**
2628 * @readonly
27- * @var \Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer\TrustedClassMethodPropertyTypeInferer
29+ * @var \Rector\Reflection\ReflectionResolver
2830 */
29- private $ trustedClassMethodPropertyTypeInferer ;
31+ private $ reflectionResolver ;
32+ /**
33+ * @readonly
34+ * @var \Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer\AllAssignNodePropertyTypeInferer
35+ */
36+ private $ allAssignNodePropertyTypeInferer ;
37+ /**
38+ * @readonly
39+ * @var \Rector\StaticTypeMapper\StaticTypeMapper
40+ */
41+ private $ staticTypeMapper ;
3042 /**
3143 * @var string
3244 */
@@ -35,9 +47,11 @@ final class TypedPropertyFromCreateMockAssignRector extends AbstractRector imple
3547 * @var string
3648 */
3749 private const MOCK_OBJECT_CLASS = 'PHPUnit \\Framework \\MockObject \\MockObject ' ;
38- public function __construct (TrustedClassMethodPropertyTypeInferer $ trustedClassMethodPropertyTypeInferer )
50+ public function __construct (ReflectionResolver $ reflectionResolver , AllAssignNodePropertyTypeInferer $ allAssignNodePropertyTypeInferer , StaticTypeMapper $ staticTypeMapper )
3951 {
40- $ this ->trustedClassMethodPropertyTypeInferer = $ trustedClassMethodPropertyTypeInferer ;
52+ $ this ->reflectionResolver = $ reflectionResolver ;
53+ $ this ->allAssignNodePropertyTypeInferer = $ allAssignNodePropertyTypeInferer ;
54+ $ this ->staticTypeMapper = $ staticTypeMapper ;
4155 }
4256 public function getRuleDefinition () : RuleDefinition
4357 {
@@ -81,6 +95,7 @@ public function refactor(Node $node) : ?Node
8195 if (!$ this ->isObjectType ($ node , new ObjectType (self ::TEST_CASE_CLASS ))) {
8296 return null ;
8397 }
98+ $ classReflection = null ;
8499 $ hasChanged = \false;
85100 foreach ($ node ->getProperties () as $ property ) {
86101 // already typed
@@ -91,11 +106,25 @@ public function refactor(Node $node) : ?Node
91106 if (!$ setUpClassMethod instanceof ClassMethod) {
92107 continue ;
93108 }
94- $ type = $ this ->trustedClassMethodPropertyTypeInferer ->inferProperty ($ node , $ property , $ setUpClassMethod );
95- if (!$ this ->isMockObjectType ($ type )) {
109+ if (!$ classReflection instanceof ClassReflection) {
110+ $ classReflection = $ this ->reflectionResolver ->resolveClassReflection ($ node );
111+ }
112+ // ClassReflection not detected, early skip
113+ if (!$ classReflection instanceof ClassReflection) {
114+ return null ;
115+ }
116+ $ type = $ this ->allAssignNodePropertyTypeInferer ->inferProperty ($ property , $ classReflection , $ this ->file );
117+ if (!$ type instanceof Type) {
118+ continue ;
119+ }
120+ $ propertyType = $ this ->staticTypeMapper ->mapPHPStanTypeToPhpParserNode ($ type , TypeKind::PROPERTY );
121+ if (!$ propertyType instanceof Node) {
96122 continue ;
97123 }
98- $ property ->type = new FullyQualified (self ::MOCK_OBJECT_CLASS );
124+ if (!$ this ->isObjectType ($ propertyType , new ObjectType (self ::MOCK_OBJECT_CLASS ))) {
125+ continue ;
126+ }
127+ $ property ->type = $ propertyType ;
99128 $ hasChanged = \true;
100129 }
101130 if (!$ hasChanged ) {
@@ -107,21 +136,4 @@ public function provideMinPhpVersion() : int
107136 {
108137 return PhpVersionFeature::TYPED_PROPERTIES ;
109138 }
110- private function isMockObjectType (Type $ type ) : bool
111- {
112- if ($ type instanceof ObjectType && $ type ->isInstanceOf (self ::MOCK_OBJECT_CLASS )->yes ()) {
113- return \true;
114- }
115- return $ this ->isIntersectionWithMockObjectType ($ type );
116- }
117- private function isIntersectionWithMockObjectType (Type $ type ) : bool
118- {
119- if (!$ type instanceof IntersectionType) {
120- return \false;
121- }
122- if (\count ($ type ->getTypes ()) !== 2 ) {
123- return \false;
124- }
125- return \in_array (self ::MOCK_OBJECT_CLASS , $ type ->getObjectClassNames ());
126- }
127139}
0 commit comments