1313use PhpParser \Node \Stmt \ClassMethod ;
1414use PhpParser \Node \Stmt \Expression ;
1515use PhpParser \Node \Stmt \Property ;
16+ use PhpParser \Node \Stmt \Trait_ ;
17+ use Rector \PhpParser \AstResolver ;
1618use Rector \PhpParser \Node \BetterNodeFinder ;
1719use Rector \PhpParser \NodeFinder \PropertyFetchFinder ;
1820use Rector \PHPUnit \CodeQuality \NodeAnalyser \MockObjectPropertyDetector ;
@@ -32,6 +34,7 @@ public function __construct(
3234 private readonly MockObjectPropertyDetector $ mockObjectPropertyDetector ,
3335 private readonly PropertyFetchFinder $ propertyFetchFinder ,
3436 private readonly BetterNodeFinder $ betterNodeFinder ,
37+ private readonly AstResolver $ astResolver ,
3538 ) {
3639 }
3740
@@ -235,9 +238,35 @@ private function resolvePropertyNamesToRemove(array $propertyNamesToCreateMockMe
235238 continue ;
236239 }
237240
241+ // the property might be used in a used trait, skip removal to stay safe
242+ if ($ this ->isPropertyUsedInTrait ($ class , $ propertyName )) {
243+ continue ;
244+ }
245+
238246 $ propertyNamesToRemove [] = $ propertyName ;
239247 }
240248
241249 return $ propertyNamesToRemove ;
242250 }
251+
252+ private function isPropertyUsedInTrait (Class_ $ class , string $ propertyName ): bool
253+ {
254+ foreach ($ class ->getTraitUses () as $ traitUse ) {
255+ foreach ($ traitUse ->traits as $ traitName ) {
256+ $ trait = $ this ->astResolver ->resolveClassFromName ($ traitName ->toString ());
257+ if (! $ trait instanceof Trait_) {
258+ continue ;
259+ }
260+
261+ $ propertyFetches = $ this ->betterNodeFinder ->findInstancesOf ($ trait , [PropertyFetch::class]);
262+ foreach ($ propertyFetches as $ propertyFetch ) {
263+ if ($ this ->isName ($ propertyFetch ->name , $ propertyName )) {
264+ return true ;
265+ }
266+ }
267+ }
268+ }
269+
270+ return false ;
271+ }
243272}
0 commit comments