diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc.php.inc new file mode 100644 index 00000000000..740997bbb4d --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc.php.inc @@ -0,0 +1,25 @@ +property = new \DateTime('now'); + } + } + + public function verify() + { + if ($this->property instanceof \DateTime) { + return true; + } + + return false; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc2.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc2.php.inc new file mode 100644 index 00000000000..0ef8f7719f8 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_property_by_var_doc2.php.inc @@ -0,0 +1,18 @@ +property instanceof \DateTime) { + return true; + } + + return false; + } +} diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Source/SomeClassWithPropertyUseVarDocblock.php b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Source/SomeClassWithPropertyUseVarDocblock.php new file mode 100644 index 00000000000..4c1f74d4ca3 --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Source/SomeClassWithPropertyUseVarDocblock.php @@ -0,0 +1,9 @@ +shouldSkipPropertyFetch($node->cond)) { + return null; + } + if ($node->stmts === []) { $this->removeNode($node); return null; @@ -89,4 +102,34 @@ public function refactor(Node $node): If_|null|array return $node->stmts; } + + private function shouldSkipPropertyFetch(Expr $expr): bool + { + /** @var PropertyFetch[]|StaticPropertyFetch[] $propertyFetches */ + $propertyFetches = $this->betterNodeFinder->findInstancesOf( + $expr, + [PropertyFetch::class, StaticPropertyFetch::class] + ); + + foreach ($propertyFetches as $propertyFetch) { + $classReflection = $this->reflectionResolver->resolveClassReflectionSourceObject($propertyFetch); + + if (! $classReflection instanceof ClassReflection) { + continue; + } + + $propertyName = (string) $this->nodeNameResolver->getName($propertyFetch); + + if (! $classReflection->hasNativeProperty($propertyName)) { + continue; + } + + $nativeProperty = $classReflection->getNativeProperty($propertyName); + if (! $nativeProperty->hasNativeType()) { + return true; + } + } + + return false; + } } diff --git a/src/Reflection/ReflectionResolver.php b/src/Reflection/ReflectionResolver.php index 153fca7f8bc..2516767b97f 100644 --- a/src/Reflection/ReflectionResolver.php +++ b/src/Reflection/ReflectionResolver.php @@ -84,9 +84,27 @@ public function resolveClassReflection(?Node $node): ?ClassReflection return $scope->getClassReflection(); } - public function resolveClassReflectionSourceObject(MethodCall|StaticCall $call): ?ClassReflection - { - $classMethod = $this->astResolver->resolveClassMethodFromCall($call); + public function resolveClassReflectionSourceObject( + MethodCall|StaticCall|PropertyFetch|StaticPropertyFetch $node + ): ?ClassReflection { + if ($node instanceof PropertyFetch || $node instanceof StaticPropertyFetch) { + $objectType = $node instanceof PropertyFetch + ? $this->nodeTypeResolver->getType($node->var) + : $this->nodeTypeResolver->getType($node->class); + + if (! $objectType instanceof TypeWithClassName) { + return null; + } + + $className = $objectType->getClassName(); + if (! $this->reflectionProvider->hasClass($className)) { + return null; + } + + return $this->reflectionProvider->getClass($className); + } + + $classMethod = $this->astResolver->resolveClassMethodFromCall($node); return $this->resolveClassReflection($classMethod); }