88use PhpParser \Node \Expr \StaticPropertyFetch ;
99use PhpParser \Node \Stmt \ClassLike ;
1010use PhpParser \Node \Stmt \Property ;
11+ use PHPStan \Reflection \ReflectionProvider ;
1112use PHPStan \Type \ThisType ;
1213use Rector \NodeNameResolver \NodeNameResolver ;
1314use Rector \NodeTypeResolver \NodeTypeResolver ;
@@ -32,12 +33,17 @@ final class UninitializedPropertyAnalyzer
3233 * @readonly
3334 */
3435 private NodeNameResolver $ nodeNameResolver ;
35- public function __construct (AstResolver $ astResolver , NodeTypeResolver $ nodeTypeResolver , ConstructorAssignDetector $ constructorAssignDetector , NodeNameResolver $ nodeNameResolver )
36+ /**
37+ * @readonly
38+ */
39+ private ReflectionProvider $ reflectionProvider ;
40+ public function __construct (AstResolver $ astResolver , NodeTypeResolver $ nodeTypeResolver , ConstructorAssignDetector $ constructorAssignDetector , NodeNameResolver $ nodeNameResolver , ReflectionProvider $ reflectionProvider )
3641 {
3742 $ this ->astResolver = $ astResolver ;
3843 $ this ->nodeTypeResolver = $ nodeTypeResolver ;
3944 $ this ->constructorAssignDetector = $ constructorAssignDetector ;
4045 $ this ->nodeNameResolver = $ nodeNameResolver ;
46+ $ this ->reflectionProvider = $ reflectionProvider ;
4147 }
4248 public function isUninitialized (Expr $ expr ): bool
4349 {
@@ -52,11 +58,16 @@ public function isUninitialized(Expr $expr): bool
5258 if ($ className === null ) {
5359 return \false;
5460 }
61+ $ propertyName = (string ) $ this ->nodeNameResolver ->getName ($ expr );
62+ // fast-path: a typed property with an explicit default is always initialized,
63+ // so we can skip the heavy class parsing below
64+ if ($ this ->hasTypedDefaultProperty ($ className , $ propertyName )) {
65+ return \false;
66+ }
5567 $ classLike = $ this ->astResolver ->resolveClassFromName ($ className );
5668 if (!$ classLike instanceof ClassLike) {
5769 return \false;
5870 }
59- $ propertyName = (string ) $ this ->nodeNameResolver ->getName ($ expr );
6071 $ property = $ classLike ->getProperty ($ propertyName );
6172 if (!$ property instanceof Property) {
6273 return \false;
@@ -69,4 +80,17 @@ public function isUninitialized(Expr $expr): bool
6980 }
7081 return !$ this ->constructorAssignDetector ->isPropertyAssigned ($ classLike , $ propertyName );
7182 }
83+ private function hasTypedDefaultProperty (string $ className , string $ propertyName ): bool
84+ {
85+ if (!$ this ->reflectionProvider ->hasClass ($ className )) {
86+ return \false;
87+ }
88+ $ classReflection = $ this ->reflectionProvider ->getClass ($ className );
89+ if (!$ classReflection ->hasNativeProperty ($ propertyName )) {
90+ return \false;
91+ }
92+ $ nativeReflectionProperty = $ classReflection ->getNativeProperty ($ propertyName )->getNativeReflection ();
93+ // an untyped property has an implicit null default, which is not an explicit initialization
94+ return $ nativeReflectionProperty ->hasType () && $ nativeReflectionProperty ->hasDefaultValue ();
95+ }
7296}
0 commit comments