From ac8f2973d8d43ea49cd08f99d9a7d640a2eb7cf1 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 25 Aug 2026 16:33:38 +0100 Subject: [PATCH] [Symfony61] Skip CommandConfigureToAttributeRector on non-constant configure() values Attribute arguments must be constant expressions, so a runtime value like $this->setDescription($this->description) cannot be inlined into #[AsCommand]. Leave such setX() calls in configure() instead of producing invalid code. Also return null when nothing could be extracted, so the rule no longer reports a no-op change. Reported in #1054. Co-authored-by: Alessandro Lai --- .../skip_non_constant_description.php.inc | 17 +++++++ .../CommandConfigureToAttributeRector.php | 51 ++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 rules-tests/Symfony61/Rector/Class_/CommandConfigureToAttributeRector/Fixture/skip_non_constant_description.php.inc diff --git a/rules-tests/Symfony61/Rector/Class_/CommandConfigureToAttributeRector/Fixture/skip_non_constant_description.php.inc b/rules-tests/Symfony61/Rector/Class_/CommandConfigureToAttributeRector/Fixture/skip_non_constant_description.php.inc new file mode 100644 index 00000000..16e68d81 --- /dev/null +++ b/rules-tests/Symfony61/Rector/Class_/CommandConfigureToAttributeRector/Fixture/skip_non_constant_description.php.inc @@ -0,0 +1,17 @@ +setDescription($this->description); + } +} diff --git a/rules/Symfony61/Rector/Class_/CommandConfigureToAttributeRector.php b/rules/Symfony61/Rector/Class_/CommandConfigureToAttributeRector.php index 9b08503a..6a8f8587 100644 --- a/rules/Symfony61/Rector/Class_/CommandConfigureToAttributeRector.php +++ b/rules/Symfony61/Rector/Class_/CommandConfigureToAttributeRector.php @@ -8,11 +8,16 @@ use PhpParser\Node\Arg; use PhpParser\Node\Attribute; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Array_; +use PhpParser\Node\Expr\ClassConstFetch; +use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; use PhpParser\Node\Name; +use PhpParser\Node\Scalar; +use PhpParser\Node\Scalar\InterpolatedString; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; @@ -185,10 +190,13 @@ function (Arg $arg): string { $asCommandAttribute->args = $attributeArgs; } + $hasChanged = $attributeArgs !== []; + // remove left overs foreach ((array) $configureClassMethod->stmts as $key => $stmt) { if ($this->isExpressionVariableThis($stmt)) { unset($configureClassMethod->stmts[$key]); + $hasChanged = true; } } @@ -197,11 +205,17 @@ function (Arg $arg): string { foreach ($node->stmts as $key => $classStmt) { if ($classStmt === $configureClassMethod) { unset($node->stmts[$key]); + $hasChanged = true; break; } } } + // nothing could be extracted (e.g. only non-constant values), leave the class untouched + if (! $hasChanged) { + return null; + } + return $node; } @@ -262,14 +276,49 @@ private function findAndRemoveMethodExpr(ClassMethod $classMethod, string $metho return null; } - $expr = $node->getArgs()[0] + $argValue = $node->getArgs()[0] ->value; + + // attribute arguments must be constant expressions; + // a runtime value (e.g. $this->description) cannot be inlined, so leave the call in place + if (! $this->isPermittedAttributeValue($argValue)) { + return null; + } + + $expr = $argValue; return $node->var; }); return $expr; } + private function isPermittedAttributeValue(Expr $expr): bool + { + if ($expr instanceof Scalar) { + return ! $expr instanceof InterpolatedString; + } + + if ($expr instanceof ConstFetch || $expr instanceof ClassConstFetch) { + return true; + } + + if ($expr instanceof Array_) { + foreach ($expr->items as $item) { + if ($item->key instanceof Expr && ! $this->isPermittedAttributeValue($item->key)) { + return false; + } + + if (! $this->isPermittedAttributeValue($item->value)) { + return false; + } + } + + return true; + } + + return false; + } + private function isExpressionVariableThis(Stmt $stmt): bool { if (! $stmt instanceof Expression) {