From 4d7585e875850f695bc312cd1a57615e980d22bd Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 14 Oct 2026 17:17:53 +0200 Subject: [PATCH 1/2] Skip PHPStan conditional return type in RemoveSuperfluousReturnNameFixer --- .../fixer/rules/symplify_docblock2.go | 5 +++ .../fixer/rules/symplify_docblock_test.go | 6 ++++ .../RemoveSuperfluousReturnNameFixer.php | 7 ++++ .../skip_conditional_return_type.php.inc | 35 +++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 packages/coding-standard/tests/Fixer/Commenting/RemoveSuperfluousReturnNameFixer/Fixture/skip_conditional_return_type.php.inc diff --git a/blink/internal/fixer/rules/symplify_docblock2.go b/blink/internal/fixer/rules/symplify_docblock2.go index 4642dc9242..ebb88f07ee 100644 --- a/blink/internal/fixer/rules/symplify_docblock2.go +++ b/blink/internal/fixer/rules/symplify_docblock2.go @@ -104,6 +104,8 @@ type RemoveSuperfluousReturnName struct{} var ( returnVarNameRe = regexp.MustCompile(`(@(?:psalm-|phpstan-)?return)(\s+[|\\\w]+)?(\s+)(\$[\w]+)`) docVariableNameRe = regexp.MustCompile(`\$\w+`) + // PHPStan conditional return type, e.g. "@return $var ? TypeA : TypeB" + conditionalReturnRe = regexp.MustCompile(`@(?:psalm-|phpstan-)?return\s+\$\w+\s*(?:\?|is\b)`) ) func (RemoveSuperfluousReturnName) Name() string { @@ -125,6 +127,9 @@ func (RemoveSuperfluousReturnName) Fix(s *tokens.Stream) bool { if m[4] == "$this" { continue } + if conditionalReturnRe.MatchString(line) { + continue + } if len(docVariableNameRe.FindAllString(line, -1)) >= 2 { continue } diff --git a/blink/internal/fixer/rules/symplify_docblock_test.go b/blink/internal/fixer/rules/symplify_docblock_test.go index 647a5192d0..668408d07b 100644 --- a/blink/internal/fixer/rules/symplify_docblock_test.go +++ b/blink/internal/fixer/rules/symplify_docblock_test.go @@ -88,6 +88,12 @@ func TestRemoveSuperfluousReturnName(t *testing.T) { t.Fatalf("changed=%v got=%q want=%q", changed, got, want) } idempotent(t, RemoveSuperfluousReturnName{}, got) + + // a PHPStan conditional return type is left alone; the variable is the condition, not a name + cond := " : array\n */\n function f(bool $arrayHydration) {}\n}\n" + if got, changed := apply(t, RemoveSuperfluousReturnName{}, cond); changed || got != cond { + t.Fatalf("conditional return type changed=%v got=%q", changed, got) + } } // TestRemoveSuperfluousVarName drops the variable name from a property @var. diff --git a/packages/coding-standard/src/Fixer/Commenting/RemoveSuperfluousReturnNameFixer.php b/packages/coding-standard/src/Fixer/Commenting/RemoveSuperfluousReturnNameFixer.php index 99b1c55aba..87075cbd6c 100644 --- a/packages/coding-standard/src/Fixer/Commenting/RemoveSuperfluousReturnNameFixer.php +++ b/packages/coding-standard/src/Fixer/Commenting/RemoveSuperfluousReturnNameFixer.php @@ -35,6 +35,9 @@ final class RemoveSuperfluousReturnNameFixer extends AbstractDocBlockFixer private const string VARIABLE_NAME_PART = 'variableName'; + // PHPStan conditional return type, e.g. "@return $var ? TypeA : TypeB" + private const string CONDITIONAL_RETURN_REGEX = '#@(?:psalm-|phpstan-)?return\s+\$\w+\s*(?:\?|is\b)#'; + public function getDefinition(): FixerDefinitionInterface { return new FixerDefinition(self::ERROR_MESSAGE, []); @@ -86,6 +89,10 @@ private function shouldSkip(array $match, string $content): bool return true; } + if (Regex::match($content, self::CONDITIONAL_RETURN_REGEX) !== null) { + return true; + } + // has multiple return values? "@return array $one, $two" return count(Regex::matchAll($content, self::VARIABLE_NAME_REGEX)) >= 2; } diff --git a/packages/coding-standard/tests/Fixer/Commenting/RemoveSuperfluousReturnNameFixer/Fixture/skip_conditional_return_type.php.inc b/packages/coding-standard/tests/Fixer/Commenting/RemoveSuperfluousReturnNameFixer/Fixture/skip_conditional_return_type.php.inc new file mode 100644 index 0000000000..dd1d74ab67 --- /dev/null +++ b/packages/coding-standard/tests/Fixer/Commenting/RemoveSuperfluousReturnNameFixer/Fixture/skip_conditional_return_type.php.inc @@ -0,0 +1,35 @@ +> : array + */ +function getLists(bool $arrayHydration = false): array +{ +} + +/** + * @phpstan-return $isPublic is true ? string : int + */ +function conditionalWithIs(bool $isPublic): string|int +{ +} + +?> +----- +> : array + */ +function getLists(bool $arrayHydration = false): array +{ +} + +/** + * @phpstan-return $isPublic is true ? string : int + */ +function conditionalWithIs(bool $isPublic): string|int +{ +} + +?> From 6d6a9fd7f56b5c8220eab8da3c8316d1bea9ed0e Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 14 Oct 2026 17:22:06 +0200 Subject: [PATCH 2/2] Also cover parenthesized conditional return type in fixtures --- .../fixer/rules/symplify_docblock_test.go | 10 ++++++--- .../skip_conditional_return_type.php.inc | 22 +++++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/blink/internal/fixer/rules/symplify_docblock_test.go b/blink/internal/fixer/rules/symplify_docblock_test.go index 668408d07b..c420cb365d 100644 --- a/blink/internal/fixer/rules/symplify_docblock_test.go +++ b/blink/internal/fixer/rules/symplify_docblock_test.go @@ -90,9 +90,13 @@ func TestRemoveSuperfluousReturnName(t *testing.T) { idempotent(t, RemoveSuperfluousReturnName{}, got) // a PHPStan conditional return type is left alone; the variable is the condition, not a name - cond := " : array\n */\n function f(bool $arrayHydration) {}\n}\n" - if got, changed := apply(t, RemoveSuperfluousReturnName{}, cond); changed || got != cond { - t.Fatalf("conditional return type changed=%v got=%q", changed, got) + for _, cond := range []string{ + " : array)\n */\n function f(bool $arrayHydration) {}\n}\n", + " : array\n */\n function f(bool $arrayHydration) {}\n}\n", + } { + if got, changed := apply(t, RemoveSuperfluousReturnName{}, cond); changed || got != cond { + t.Fatalf("conditional return type changed=%v got=%q", changed, got) + } } } diff --git a/packages/coding-standard/tests/Fixer/Commenting/RemoveSuperfluousReturnNameFixer/Fixture/skip_conditional_return_type.php.inc b/packages/coding-standard/tests/Fixer/Commenting/RemoveSuperfluousReturnNameFixer/Fixture/skip_conditional_return_type.php.inc index dd1d74ab67..0317b412ca 100644 --- a/packages/coding-standard/tests/Fixer/Commenting/RemoveSuperfluousReturnNameFixer/Fixture/skip_conditional_return_type.php.inc +++ b/packages/coding-standard/tests/Fixer/Commenting/RemoveSuperfluousReturnNameFixer/Fixture/skip_conditional_return_type.php.inc @@ -1,35 +1,49 @@ > : array + * @return ($arrayHydration is true ? array> : array) */ function getLists(bool $arrayHydration = false): array { } /** - * @phpstan-return $isPublic is true ? string : int + * @phpstan-return ($isPublic is true ? string : int) */ function conditionalWithIs(bool $isPublic): string|int { } +/** + * @return $arrayHydration ? array : array + */ +function bareConditional(bool $arrayHydration = false): array +{ +} + ?> ----- > : array + * @return ($arrayHydration is true ? array> : array) */ function getLists(bool $arrayHydration = false): array { } /** - * @phpstan-return $isPublic is true ? string : int + * @phpstan-return ($isPublic is true ? string : int) */ function conditionalWithIs(bool $isPublic): string|int { } +/** + * @return $arrayHydration ? array : array + */ +function bareConditional(bool $arrayHydration = false): array +{ +} + ?>