1212use PhpParser \Node \Stmt \ClassMethod ;
1313use PhpParser \Node \Stmt \Expression ;
1414use PhpParser \Node \Stmt \Property ;
15- use Rector \PHPUnit \CodeQuality \NodeAnalyser \MockObjectExprDetector ;
15+ use Rector \PhpParser \Node \BetterNodeFinder ;
16+ use Rector \PhpParser \NodeFinder \PropertyFetchFinder ;
1617use Rector \PHPUnit \CodeQuality \NodeAnalyser \MockObjectPropertyDetector ;
1718use Rector \PHPUnit \NodeAnalyzer \TestsNodeAnalyzer ;
1819use Rector \Rector \AbstractRector ;
@@ -28,7 +29,8 @@ final class RemoveNeverUsedMockPropertyRector extends AbstractRector
2829 public function __construct (
2930 private readonly TestsNodeAnalyzer $ testsNodeAnalyzer ,
3031 private readonly MockObjectPropertyDetector $ mockObjectPropertyDetector ,
31- private readonly MockObjectExprDetector $ mockObjectExprDetector ,
32+ private readonly PropertyFetchFinder $ propertyFetchFinder ,
33+ private readonly BetterNodeFinder $ betterNodeFinder ,
3234 ) {
3335 }
3436
@@ -102,15 +104,7 @@ public function refactor(Node $node): ?Node
102104 return null ;
103105 }
104106
105- $ propertyNamesToRemove = [];
106- foreach (array_keys ($ propertyNamesToCreateMockMethodCalls ) as $ propertyName ) {
107- if ($ this ->mockObjectExprDetector ->isPropertyMockObjectPassedAsArgument ($ node , $ propertyName )) {
108- continue ;
109- }
110-
111- $ propertyNamesToRemove [] = $ propertyName ;
112- }
113-
107+ $ propertyNamesToRemove = $ this ->resolvePropertyNamesToRemove ($ propertyNamesToCreateMockMethodCalls , $ node );
114108 if ($ propertyNamesToRemove === []) {
115109 return null ;
116110 }
@@ -197,4 +191,47 @@ private function removePropertyFromClass(Class_ $class, string $propertyNameToRe
197191 unset($ class ->stmts [$ key ]);
198192 }
199193 }
194+
195+ /**
196+ * @param array<string, MethodCall> $propertyNamesToCreateMockMethodCalls
197+ * @return string[]
198+ */
199+ private function resolvePropertyNamesToRemove (array $ propertyNamesToCreateMockMethodCalls , Class_ $ class ): array
200+ {
201+ $ propertyNamesToRemove = [];
202+ foreach (array_keys ($ propertyNamesToCreateMockMethodCalls ) as $ propertyName ) {
203+ $ allPropertyFetches = $ this ->propertyFetchFinder ->findLocalPropertyFetchesByName ($ class , $ propertyName );
204+
205+ /** @var MethodCall[] $methodCalls */
206+ $ methodCalls = $ this ->betterNodeFinder ->findInstancesOfScoped ($ class ->getMethods (), MethodCall::class);
207+
208+ $ propertyFetchesMethodCalls = [];
209+ foreach ($ methodCalls as $ methodCall ) {
210+ if ($ methodCall ->isFirstClassCallable ()) {
211+ continue ;
212+ }
213+
214+ if (! $ methodCall ->var instanceof PropertyFetch) {
215+ continue ;
216+ }
217+
218+ $ propertyFetch = $ methodCall ->var ;
219+ if (! $ this ->isName ($ propertyFetch ->name , $ propertyName )) {
220+ continue ;
221+ }
222+
223+ // used in method call, skip removal
224+ $ propertyFetchesMethodCalls [] = $ methodCall ;
225+ }
226+
227+ // -1 for the assign in setUp() method
228+ if ((count ($ allPropertyFetches ) - 1 ) !== count ($ propertyFetchesMethodCalls )) {
229+ continue ;
230+ }
231+
232+ $ propertyNamesToRemove [] = $ propertyName ;
233+ }
234+
235+ return $ propertyNamesToRemove ;
236+ }
200237}
0 commit comments