diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_unset_by_trait.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_unset_by_trait.php.inc new file mode 100644 index 00000000000..043cc1385cc --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Fixture/skip_unset_by_trait.php.inc @@ -0,0 +1,23 @@ +entityManager = $entityManager; + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/TraitWithUnsetProperty.php b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/TraitWithUnsetProperty.php new file mode 100644 index 00000000000..1b28b6ab5d3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromStrictConstructorRector/Source/TraitWithUnsetProperty.php @@ -0,0 +1,11 @@ +entityManager); + } +} diff --git a/src/NodeAnalyzer/PropertyFetchAnalyzer.php b/src/NodeAnalyzer/PropertyFetchAnalyzer.php index 1a1f6f8d10f..caa16cf4053 100644 --- a/src/NodeAnalyzer/PropertyFetchAnalyzer.php +++ b/src/NodeAnalyzer/PropertyFetchAnalyzer.php @@ -21,8 +21,10 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\Type\ObjectType; use PHPStan\Type\ThisType; +use Rector\DeadCode\NodeAnalyzer\PropertyWriteonlyAnalyzer; use Rector\Enum\ObjectReference; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\NodeNestingScope\ContextAnalyzer; use Rector\NodeTypeResolver\NodeTypeResolver; use Rector\PhpParser\AstResolver; use Rector\PhpParser\Node\BetterNodeFinder; @@ -41,7 +43,9 @@ public function __construct( private BetterNodeFinder $betterNodeFinder, private AstResolver $astResolver, private NodeTypeResolver $nodeTypeResolver, - private ReflectionResolver $reflectionResolver + private ReflectionResolver $reflectionResolver, + private ContextAnalyzer $contextAnalyzer, + private PropertyWriteonlyAnalyzer $propertyWriteonlyAnalyzer ) { } @@ -113,11 +117,18 @@ public function containsWrittenPropertyFetchName(Trait_ $trait, string $property return (bool) $this->betterNodeFinder->findFirst( $trait, function (Node $node) use ($propertyName): bool { - if (! $node instanceof Assign) { + if (! $this->isLocalPropertyFetchName($node, $propertyName)) { return false; } - return $this->isLocalPropertyFetchName($node->var, $propertyName); + /** + * @var PropertyFetch|StaticPropertyFetch|NullsafePropertyFetch $node + */ + if ($this->contextAnalyzer->isChangeableContext($node)) { + return true; + } + + return $this->propertyWriteonlyAnalyzer->arePropertyFetchesExclusivelyBeingAssignedTo([$node]); } ); } diff --git a/src/NodeManipulator/PropertyManipulator.php b/src/NodeManipulator/PropertyManipulator.php index 185d73cb805..5d640aa53e8 100644 --- a/src/NodeManipulator/PropertyManipulator.php +++ b/src/NodeManipulator/PropertyManipulator.php @@ -20,6 +20,7 @@ use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; +use Rector\NodeNestingScope\ContextAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; @@ -59,7 +60,8 @@ public function __construct( private PromotedPropertyResolver $promotedPropertyResolver, private ConstructorAssignDetector $constructorAssignDetector, private AstResolver $astResolver, - private PropertyFetchAnalyzer $propertyFetchAnalyzer + private PropertyFetchAnalyzer $propertyFetchAnalyzer, + private ContextAnalyzer $contextAnalyzer ) { } @@ -78,7 +80,7 @@ public function isPropertyChangeableExceptConstructor( $classMethod = $class->getMethod(MethodName::CONSTRUCT); foreach ($propertyFetches as $propertyFetch) { - if ($this->isChangeableContext($propertyFetch)) { + if ($this->contextAnalyzer->isChangeableContext($propertyFetch)) { return true; } @@ -189,23 +191,6 @@ private function isPropertyAssignedOnlyInConstructor( return $this->constructorAssignDetector->isPropertyAssigned($class, $propertyName); } - private function isChangeableContext(PropertyFetch | StaticPropertyFetch $propertyFetch): bool - { - if ($propertyFetch->getAttribute(AttributeKey::IS_UNSET_VAR, false)) { - return true; - } - - if ($propertyFetch->getAttribute(AttributeKey::INSIDE_ARRAY_DIM_FETCH, false)) { - return true; - } - - if ($propertyFetch->getAttribute(AttributeKey::IS_USED_AS_ARG_BY_REF_VALUE, false) === true) { - return true; - } - - return $propertyFetch->getAttribute(AttributeKey::IS_INCREMENT_OR_DECREMENT, false) === true; - } - private function hasAllowedNotReadonlyAnnotationOrAttribute(PhpDocInfo $phpDocInfo, Class_ $class): bool { if ($phpDocInfo->hasByAnnotationClasses(self::ALLOWED_NOT_READONLY_CLASS_ANNOTATIONS)) { diff --git a/src/NodeNestingScope/ContextAnalyzer.php b/src/NodeNestingScope/ContextAnalyzer.php index 3310a86718f..58fe8ffb9c0 100644 --- a/src/NodeNestingScope/ContextAnalyzer.php +++ b/src/NodeNestingScope/ContextAnalyzer.php @@ -5,6 +5,11 @@ namespace Rector\NodeNestingScope; use PhpParser\Node; +use PhpParser\Node\Expr\NullsafePropertyFetch; +use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticPropertyFetch; +use Rector\DeadCode\NodeAnalyzer\PropertyWriteonlyAnalyzer; +use Rector\NodeManipulator\AssignManipulator; use Rector\NodeTypeResolver\Node\AttributeKey; final class ContextAnalyzer @@ -24,4 +29,21 @@ public function isInIf(Node $node): bool { return $node->getAttribute(AttributeKey::IS_IN_IF) === true; } + + public function isChangeableContext(PropertyFetch | StaticPropertyFetch | NullsafePropertyFetch $propertyFetch): bool + { + if ($propertyFetch->getAttribute(AttributeKey::IS_UNSET_VAR, false)) { + return true; + } + + if ($propertyFetch->getAttribute(AttributeKey::INSIDE_ARRAY_DIM_FETCH, false)) { + return true; + } + + if ($propertyFetch->getAttribute(AttributeKey::IS_USED_AS_ARG_BY_REF_VALUE, false) === true) { + return true; + } + + return $propertyFetch->getAttribute(AttributeKey::IS_INCREMENT_OR_DECREMENT, false) === true; + } }