From 79a28aae2ae896aa1d8bdc3ae4895cfcf758035a Mon Sep 17 00:00:00 2001 From: Dmytro Androshchuk Date: Thu, 7 May 2026 14:15:14 +0300 Subject: [PATCH] fix(CommandHelpToAttributeRector): support concatenated string in setHelp() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the rule only handled plain String_ literals. When setHelp() received a Concat expression (e.g. "line one\n" . "line two") it silently dropped the call without migrating the text to #[AsCommand(help: ...)], leaving the help text permanently lost. Introduce resolveStringExpr() that recursively folds a Concat tree of String_ literals into a single string value. Non-literal sub-expressions (variables, function calls, …) cause the method to return null, which now also prevents the setHelp() removal — fixing the silent data-loss bug. --- .../add_help_concatenated_string.php.inc | 46 +++++++++++++++++++ .../Class_/CommandHelpToAttributeRector.php | 37 +++++++++++++-- 2 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 rules-tests/Symfony73/Rector/Class_/CommandHelpToAttributeRector/Fixture/add_help_concatenated_string.php.inc diff --git a/rules-tests/Symfony73/Rector/Class_/CommandHelpToAttributeRector/Fixture/add_help_concatenated_string.php.inc b/rules-tests/Symfony73/Rector/Class_/CommandHelpToAttributeRector/Fixture/add_help_concatenated_string.php.inc new file mode 100644 index 000000000..8037f191b --- /dev/null +++ b/rules-tests/Symfony73/Rector/Class_/CommandHelpToAttributeRector/Fixture/add_help_concatenated_string.php.inc @@ -0,0 +1,46 @@ +setHelp("First line of help.\n" + . "Second line of help.\n" + . "Third line of help.") + ->addOption('run', null, InputOption::VALUE_NONE, 'Run mode'); + } +} + +?> +----- +addOption('run', null, InputOption::VALUE_NONE, 'Run mode'); + } +} + +?> diff --git a/rules/Symfony73/Rector/Class_/CommandHelpToAttributeRector.php b/rules/Symfony73/Rector/Class_/CommandHelpToAttributeRector.php index ff0be791b..0c0e09a09 100644 --- a/rules/Symfony73/Rector/Class_/CommandHelpToAttributeRector.php +++ b/rules/Symfony73/Rector/Class_/CommandHelpToAttributeRector.php @@ -8,6 +8,7 @@ use PhpParser\Node\Arg; use PhpParser\Node\Attribute; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\BinaryOp\Concat; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; @@ -150,6 +151,7 @@ public function refactor(Node $node): ?Node /** * Returns the argument passed to setHelp() and removes the MethodCall node. + * Supports plain string literals and concatenated string expressions. */ private function findAndRemoveSetHelpExpr(ClassMethod $configureClassMethod): ?String_ { @@ -174,12 +176,14 @@ function (Node $node) use (&$helpString): int|null|Expr { return null; } - $argExpr = $node->getArgs()[0] - ->value; - if ($argExpr instanceof String_) { - $helpString = $argExpr; + $argExpr = $node->getArgs()[0]->value; + $resolvedValue = $this->resolveStringExpr($argExpr); + if ($resolvedValue === null) { + return null; } + $helpString = new String_($resolvedValue); + $parent = $node->getAttribute('parent'); if ($parent instanceof Expression) { unset($parent); @@ -198,6 +202,31 @@ function (Node $node) use (&$helpString): int|null|Expr { return $helpString; } + /** + * Resolves a scalar string expression — a plain String_ literal or a tree + * of Concat nodes — to its runtime string value. Returns null for any + * expression that contains non-literal parts (variables, function calls, …). + */ + private function resolveStringExpr(Expr $expr): ?string + { + if ($expr instanceof String_) { + return $expr->value; + } + + if ($expr instanceof Concat) { + $left = $this->resolveStringExpr($expr->left); + $right = $this->resolveStringExpr($expr->right); + + if ($left === null || $right === null) { + return null; + } + + return $left . $right; + } + + return null; + } + private function isExpressionVariableThis(Stmt $stmt): bool { if (! $stmt instanceof Expression) {