1212use PhpParser \Node \Expr \FuncCall ;
1313use PhpParser \Node \Expr \Include_ ;
1414use PhpParser \Node \Expr \MethodCall ;
15- use PhpParser \Node \Expr \New_ ;
1615use PhpParser \Node \Expr \NullsafeMethodCall ;
1716use PhpParser \Node \Expr \StaticCall ;
1817use PhpParser \Node \Expr \Variable ;
18+ use PhpParser \Node \Name ;
1919use PhpParser \Node \Stmt ;
2020use PhpParser \Node \Stmt \Class_ ;
2121use PhpParser \Node \Stmt \ClassMethod ;
2222use PhpParser \Node \Stmt \Expression ;
2323use PhpParser \Node \Stmt \Function_ ;
2424use PhpParser \NodeVisitor ;
25+ use PHPStan \Reflection \AttributeReflection ;
2526use PHPStan \Reflection \ClassReflection ;
27+ use PHPStan \Reflection \ReflectionProvider ;
2628use PHPStan \Type \ObjectType ;
2729use Rector \DeadCode \SideEffect \SideEffectNodeDetector ;
2830use Rector \NodeAnalyzer \VariableAnalyzer ;
2931use Rector \NodeManipulator \StmtsManipulator ;
3032use Rector \Php \ReservedKeywordAnalyzer ;
31- use Rector \Php80 \NodeAnalyzer \PhpAttributeAnalyzer ;
32- use Rector \PhpParser \AstResolver ;
3333use Rector \PhpParser \Enum \NodeGroup ;
3434use Rector \PhpParser \Node \BetterNodeFinder ;
35+ use Rector \PHPStan \ScopeFetcher ;
3536use Rector \Rector \AbstractRector ;
3637use Rector \ValueObject \MethodName ;
3738use Symplify \RuleDocGenerator \ValueObject \CodeSample \CodeSample ;
@@ -64,20 +65,15 @@ final class RemoveUnusedVariableAssignRector extends AbstractRector
6465 /**
6566 * @readonly
6667 */
67- private AstResolver $ astResolver ;
68- /**
69- * @readonly
70- */
71- private PhpAttributeAnalyzer $ phpAttributeAnalyzer ;
72- public function __construct (ReservedKeywordAnalyzer $ reservedKeywordAnalyzer , SideEffectNodeDetector $ sideEffectNodeDetector , VariableAnalyzer $ variableAnalyzer , BetterNodeFinder $ betterNodeFinder , StmtsManipulator $ stmtsManipulator , AstResolver $ astResolver , PhpAttributeAnalyzer $ phpAttributeAnalyzer )
68+ private ReflectionProvider $ reflectionProvider ;
69+ public function __construct (ReservedKeywordAnalyzer $ reservedKeywordAnalyzer , SideEffectNodeDetector $ sideEffectNodeDetector , VariableAnalyzer $ variableAnalyzer , BetterNodeFinder $ betterNodeFinder , StmtsManipulator $ stmtsManipulator , ReflectionProvider $ reflectionProvider )
7370 {
7471 $ this ->reservedKeywordAnalyzer = $ reservedKeywordAnalyzer ;
7572 $ this ->sideEffectNodeDetector = $ sideEffectNodeDetector ;
7673 $ this ->variableAnalyzer = $ variableAnalyzer ;
7774 $ this ->betterNodeFinder = $ betterNodeFinder ;
7875 $ this ->stmtsManipulator = $ stmtsManipulator ;
79- $ this ->astResolver = $ astResolver ;
80- $ this ->phpAttributeAnalyzer = $ phpAttributeAnalyzer ;
76+ $ this ->reflectionProvider = $ reflectionProvider ;
8177 }
8278 public function getRuleDefinition (): RuleDefinition
8379 {
@@ -253,11 +249,8 @@ private function resolvedAssignedVariablesByStmtPosition(array $stmts): array
253249 if ($ this ->shouldSkipVariable ($ assign ->var , $ variableName , $ refVariableNames )) {
254250 continue ;
255251 }
256- if ($ assign ->expr instanceof FuncCall || $ assign ->expr instanceof StaticCall || $ assign ->expr instanceof MethodCall || $ assign ->expr instanceof New_ || $ assign ->expr instanceof NullsafeMethodCall) {
257- $ targetCall = $ this ->astResolver ->resolveClassMethodOrFunctionFromCall ($ assign ->expr );
258- if (($ targetCall instanceof ClassMethod || $ targetCall instanceof Function_) && $ this ->phpAttributeAnalyzer ->hasPhpAttribute ($ targetCall , 'NoDiscard ' )) {
259- continue ;
260- }
252+ if ($ this ->isNoDiscardCall ($ assign ->expr )) {
253+ continue ;
261254 }
262255 $ assignedVariableNamesByStmtPosition [$ key ] = $ variableName ;
263256 }
@@ -276,4 +269,55 @@ private function shouldSkipVariable(Variable $variable, string $variableName, ar
276269 }
277270 return in_array ($ variableName , $ refVariableNames , \true);
278271 }
272+ private function isNoDiscardCall (Expr $ expr ): bool
273+ {
274+ if ($ expr instanceof FuncCall) {
275+ $ name = $ this ->getName ($ expr );
276+ if ($ name === null ) {
277+ return \false;
278+ }
279+ $ scope = ScopeFetcher::fetch ($ expr );
280+ if (!$ this ->reflectionProvider ->hasFunction (new Name ($ name ), $ scope )) {
281+ return \false;
282+ }
283+ return $ this ->hasNoDiscardAttribute ($ this ->reflectionProvider ->getFunction (new Name ($ name ), $ scope )->getAttributes ());
284+ }
285+ if ($ expr instanceof StaticCall) {
286+ $ classNames = $ this ->getType ($ expr ->class )->getObjectClassNames ();
287+ $ methodName = $ this ->getName ($ expr ->name );
288+ } elseif ($ expr instanceof MethodCall || $ expr instanceof NullsafeMethodCall) {
289+ $ classNames = $ this ->getType ($ expr ->var )->getObjectClassNames ();
290+ $ methodName = $ this ->getName ($ expr ->name );
291+ } else {
292+ return \false;
293+ }
294+ if ($ classNames === [] || $ methodName === null ) {
295+ return \false;
296+ }
297+ foreach ($ classNames as $ className ) {
298+ if (!$ this ->reflectionProvider ->hasClass ($ className )) {
299+ continue ;
300+ }
301+ $ classReflection = $ this ->reflectionProvider ->getClass ($ className );
302+ if (!$ classReflection ->hasNativeMethod ($ methodName )) {
303+ continue ;
304+ }
305+ if ($ this ->hasNoDiscardAttribute ($ classReflection ->getNativeMethod ($ methodName )->getAttributes ())) {
306+ return \true;
307+ }
308+ }
309+ return \false;
310+ }
311+ /**
312+ * @param AttributeReflection[] $attributes
313+ */
314+ private function hasNoDiscardAttribute (array $ attributes ): bool
315+ {
316+ foreach ($ attributes as $ attribute ) {
317+ if ($ attribute ->getName () === 'NoDiscard ' ) {
318+ return \true;
319+ }
320+ }
321+ return \false;
322+ }
279323}
0 commit comments