From 85875e6a327eef4150caf362ad338b166e685eba Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 8 Oct 2026 19:33:56 +0200 Subject: [PATCH 1/2] simplify scope resolver on trait traverse --- .../Rector/Array_/ArrayToFirstClassCallableRector.php | 4 ++++ .../PHPStan/Scope/PHPStanNodeScopeResolver.php | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rules/Php81/Rector/Array_/ArrayToFirstClassCallableRector.php b/rules/Php81/Rector/Array_/ArrayToFirstClassCallableRector.php index 88dae554450..75f043919ee 100644 --- a/rules/Php81/Rector/Array_/ArrayToFirstClassCallableRector.php +++ b/rules/Php81/Rector/Array_/ArrayToFirstClassCallableRector.php @@ -98,6 +98,10 @@ public function refactor(Node $node): StaticCall|MethodCall|null return null; } + if ($node->getAttribute(AttributeKey::IS_ARRAY_IN_ATTRIBUTE) === true) { + return null; + } + $scope = ScopeFetcher::fetch($node); $arrayCallable = $this->arrayCallableMethodMatcher->match($node, $scope); diff --git a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php index 35865675fbf..30c0857ef8c 100644 --- a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php +++ b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php @@ -207,8 +207,6 @@ public function processNodes( return; } - $this->decorateNodeAttrGroups($node, $mutatingScope, $nodeCallback); - if (( $node instanceof Expression || $node instanceof Return_ || @@ -466,8 +464,8 @@ private function nodeScopeResolverProcessNodes( try { $this->nodeScopeResolver->processNodes($stmts, $mutatingScope, $nodeCallback); } catch (ParserErrorsException|ParserException|ShouldNotHappenException|UndefinedVariableException) { - // nothing we can do more precise here as error parsing from deep internal PHPStan service with service injection we cannot reset - // in the middle of process + // nothing we can do more precise here as error parsing from deep internal PHPStan + // service with service injection we cannot reset in the middle of process // fallback to fill by found scope RectorNodeScopeResolver::processNodes($stmts, $mutatingScope); } @@ -736,6 +734,5 @@ private function processTrait(Trait_ $trait, MutatingScope $mutatingScope, calla $trait->setAttribute(AttributeKey::SCOPE, $traitScope); $this->nodeScopeResolverProcessNodes($trait->stmts, $traitScope, $nodeCallback); - $this->decorateNodeAttrGroups($trait, $traitScope, $nodeCallback); } } From 58b906455d868906939a0dbc610badcb5296eb8a Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 8 Oct 2026 19:42:44 +0200 Subject: [PATCH 2/2] avoid double enter of class --- .../Scope/PHPStanNodeScopeResolver.php | 47 ------------------- .../Issues/Issue7374/Fixture/fixture.php.inc | 8 ++++ .../Issue7374/Source/BusibooksBusinesses.php | 9 ++++ .../Issue7374/config/configured_rule.php | 5 +- 4 files changed, 21 insertions(+), 48 deletions(-) create mode 100644 tests/Issues/Issue7374/Source/BusibooksBusinesses.php diff --git a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php index 30c0857ef8c..780e7c2e0ee 100644 --- a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php +++ b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php @@ -10,7 +10,6 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ArrayDimFetch; -use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\AssignOp; use PhpParser\Node\Expr\AssignRef; @@ -21,7 +20,6 @@ use PhpParser\Node\Expr\Cast; use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\Clone_; -use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\Empty_; use PhpParser\Node\Expr\ErrorSuppress; @@ -56,13 +54,9 @@ use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\NullableType; -use PhpParser\Node\Param; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Catch_; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassConst; -use PhpParser\Node\Stmt\ClassLike; -use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Do_; use PhpParser\Node\Stmt\Echo_; use PhpParser\Node\Stmt\ElseIf_; @@ -72,7 +66,6 @@ use PhpParser\Node\Stmt\Finally_; use PhpParser\Node\Stmt\For_; use PhpParser\Node\Stmt\Foreach_; -use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Interface_; use PhpParser\Node\Stmt\Property; @@ -526,40 +519,6 @@ private function processArrayItem(ArrayItem $arrayItem, MutatingScope $mutatingS } } - /** - * @param callable(Node $trait, MutatingScope $scope): void $nodeCallback - */ - private function decorateNodeAttrGroups(Node $node, MutatingScope $mutatingScope, callable $nodeCallback): void - { - // better to have AttrGroupsAwareInterface for all Node definition with attrGroups property - // but because may conflict with StmtsAwareInterface patch, this needs to be here - if ( - ! $node instanceof Param && - ! $node instanceof ArrowFunction && - ! $node instanceof Closure && - ! $node instanceof ClassConst && - ! $node instanceof ClassLike && - ! $node instanceof ClassMethod && - ! $node instanceof EnumCase && - ! $node instanceof Function_ && - ! $node instanceof Property - ) { - return; - } - - foreach ($node->attrGroups as $attrGroup) { - foreach ($attrGroup->attrs as $attr) { - foreach ($attr->args as $arg) { - $this->nodeScopeResolverProcessNodes( - [new Expression($arg->value)], - $mutatingScope, - $nodeCallback - ); - } - } - } - } - private function processSwitch(Switch_ $switch, MutatingScope $mutatingScope): void { $switch->cond->setAttribute(AttributeKey::SCOPE, $mutatingScope); @@ -680,11 +639,6 @@ private function resolveClassOrInterfaceScope( $context = $this->privatesAccessor->getPrivateProperty($mutatingScope, 'context'); $this->privatesAccessor->setPrivateProperty($context, 'classReflection', null); - try { - return $mutatingScope->enterClass($classReflection); - } catch (ShouldNotHappenException) { - } - return $mutatingScope; } @@ -711,7 +665,6 @@ private function processTrait(Trait_ $trait, MutatingScope $mutatingScope, calla if (! $this->reflectionProvider->hasClass($traitName)) { $trait->setAttribute(AttributeKey::SCOPE, $mutatingScope); $this->nodeScopeResolverProcessNodes($trait->stmts, $mutatingScope, $nodeCallback); - $this->decorateNodeAttrGroups($trait, $mutatingScope, $nodeCallback); return; } diff --git a/tests/Issues/Issue7374/Fixture/fixture.php.inc b/tests/Issues/Issue7374/Fixture/fixture.php.inc index 1d22fc1e29f..ca851988bf8 100644 --- a/tests/Issues/Issue7374/Fixture/fixture.php.inc +++ b/tests/Issues/Issue7374/Fixture/fixture.php.inc @@ -2,6 +2,8 @@ namespace Rector\Tests\Issues\Issue7374\Fixture; +use Rector\Tests\Issues\Issue7374\Source\BusibooksBusinesses; + final class ClassWithTwoMethods { private function displayTree($name, $data, $other = null) @@ -19,11 +21,15 @@ final class ClassWithTwoMethods )); } } + +?> ----- displayTree($equity); } } + +?> diff --git a/tests/Issues/Issue7374/Source/BusibooksBusinesses.php b/tests/Issues/Issue7374/Source/BusibooksBusinesses.php new file mode 100644 index 00000000000..d5522f3c919 --- /dev/null +++ b/tests/Issues/Issue7374/Source/BusibooksBusinesses.php @@ -0,0 +1,9 @@ +withRules([RemoveUnusedPrivateMethodParameterRector::class, RenameVariableToMatchNewTypeRector::class]); + ->withRules([ + RemoveUnusedPrivateMethodParameterRector::class, + RenameVariableToMatchNewTypeRector::class, + ]);