From 2d045f6fa5b4de0f9b56605388c6a71717c4ce7e Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 27 Nov 2025 16:12:53 +0100 Subject: [PATCH 1/3] [issue 9492] create reproducer for modified array_map args, as creates changed args on print --- composer.json | 2 +- tests/PhpParser/Printer/PHPStanPrinterTest.php | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 26cb49af90e..5b1e9140662 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "nikic/php-parser": "^5.6.2", "ondram/ci-detector": "^4.2", "phpstan/phpdoc-parser": "^2.3", - "phpstan/phpstan": "^2.1.32", + "phpstan/phpstan": "2.1.19 as 2.1.32", "react/event-loop": "^1.6", "react/promise": "^3.3", "react/socket": "^1.17", diff --git a/tests/PhpParser/Printer/PHPStanPrinterTest.php b/tests/PhpParser/Printer/PHPStanPrinterTest.php index 15a021fa855..d5ab1d6901b 100644 --- a/tests/PhpParser/Printer/PHPStanPrinterTest.php +++ b/tests/PhpParser/Printer/PHPStanPrinterTest.php @@ -8,7 +8,10 @@ use PHPStan\Parser\Parser; use PHPStan\Parser\RichParser; use Rector\Testing\PHPUnit\AbstractLazyTestCase; +<<<<<<< HEAD use ReflectionProperty; +======= +>>>>>>> ea9a6ad8a5 ([issue 9492] create reproducer for modified array_map args, as creates changed args on print) /** * Test case for: https://github.com/rectorphp/rector/issues/9492 @@ -24,17 +27,30 @@ public function testAddingCommentOnSomeNodesFail(): void $stmts = $phpstanParser->parseFile(__DIR__ . '/Fixture/some_array_map.php'); // get private property "parser" +<<<<<<< HEAD $parserReflectionProperty = new ReflectionProperty(RichParser::class, 'parser'); +======= + $parserReflectionProperty = new \ReflectionProperty(RichParser::class, 'parser'); +>>>>>>> ea9a6ad8a5 ([issue 9492] create reproducer for modified array_map args, as creates changed args on print) /** @var \PhpParser\Parser $innerParser */ $innerParser = $parserReflectionProperty->getValue($phpstanParser); $tokens = $innerParser->getTokens(); +<<<<<<< HEAD $standard = new Standard([]); $printerContents = $standard->printFormatPreserving($stmts, $stmts, $tokens); $newlineNormalizedContents = str_replace("\r\n", PHP_EOL, $printerContents); $this->assertStringEqualsFile(__DIR__ . '/Fixture/some_array_map.php', $newlineNormalizedContents); +======= + $standardPrinter = new Standard([ + 'newline' => "\n", + ]); + $printerContents = $standardPrinter->printFormatPreserving($stmts, $stmts, $tokens); + + $this->assertStringEqualsFile(__DIR__ . '/Fixture/some_array_map.php', $printerContents); +>>>>>>> ea9a6ad8a5 ([issue 9492] create reproducer for modified array_map args, as creates changed args on print) } } From 3d0786de0e8970a42810f6f2ba80283693efafeb Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 27 Nov 2025 16:48:47 +0100 Subject: [PATCH 2/3] try to remove breaking service from container --- composer.json | 2 +- .../Enum_/EnumCaseToPascalCaseRector.php | 6 +-- .../PHPStan/PHPStanContainerMemento.php | 45 +++++++++++++++++++ src/PhpParser/Parser/RectorParser.php | 7 +++ .../PhpParser/Printer/PHPStanPrinterTest.php | 21 +++------ 5 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 src/DependencyInjection/PHPStan/PHPStanContainerMemento.php diff --git a/composer.json b/composer.json index 5b1e9140662..26cb49af90e 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "nikic/php-parser": "^5.6.2", "ondram/ci-detector": "^4.2", "phpstan/phpdoc-parser": "^2.3", - "phpstan/phpstan": "2.1.19 as 2.1.32", + "phpstan/phpstan": "^2.1.32", "react/event-loop": "^1.6", "react/promise": "^3.3", "react/socket": "^1.17", diff --git a/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php b/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php index e3b48e80ded..93def346301 100644 --- a/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php +++ b/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php @@ -208,9 +208,9 @@ private function convertToPascalCase(string $name): string fn ($part): string => // If part is all uppercase, convert to ucfirst(strtolower()) // If part is already mixed or PascalCase, keep as is except ucfirst - ctype_upper($part) - ? ucfirst(strtolower($part)) - : ucfirst($part), + ctype_upper((string) $part) + ? ucfirst(strtolower((string) $part)) + : ucfirst((string) $part), $parts ) ); diff --git a/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php b/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php new file mode 100644 index 00000000000..9934a7556db --- /dev/null +++ b/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php @@ -0,0 +1,45 @@ +getPrivateProperty($richParser, 'container'); + + /** @var NetteContainer $originalContainer */ + $originalContainer = $privatesAccessor->getPrivateProperty($container, 'originalContainer'); + + /** @var NetteContainer $originalContainer */ + $deeperContainer = $privatesAccessor->getPrivateProperty($originalContainer, 'container'); + + // get tags property + $tags = $privatesAccessor->getPrivateProperty($deeperContainer, 'tags'); + + // keep only the anonymous class visitor + // remove all the rest, https://github.com/phpstan/phpstan-src/tree/1d86de8bb9371534983a8dbcd879e057d2ff028f/src/Parser + $tags[RichParser::VISITOR_SERVICE_TAG] = [ + $container->findServiceNamesByType(AnonymousClassVisitor::class)[0] => true, + ]; + + $privatesAccessor->setPrivateProperty($deeperContainer, 'tags', $tags); + } +} diff --git a/src/PhpParser/Parser/RectorParser.php b/src/PhpParser/Parser/RectorParser.php index 3594e2d00de..81f72aeb206 100644 --- a/src/PhpParser/Parser/RectorParser.php +++ b/src/PhpParser/Parser/RectorParser.php @@ -8,15 +8,22 @@ use PhpParser\ParserFactory; use PhpParser\PhpVersion; use PHPStan\Parser\Parser; +use PHPStan\Parser\RichParser; +use Rector\DependencyInjection\PHPStan\PHPStanContainerMemento; use Rector\PhpParser\ValueObject\StmtsAndTokens; use Rector\Util\Reflection\PrivatesAccessor; final readonly class RectorParser { + /** + * @param RichParser $parser + */ public function __construct( private Parser $parser, private PrivatesAccessor $privatesAccessor ) { + + PHPStanContainerMemento::removeRichVisitors($parser); } /** diff --git a/tests/PhpParser/Printer/PHPStanPrinterTest.php b/tests/PhpParser/Printer/PHPStanPrinterTest.php index d5ab1d6901b..d95bdafaf0d 100644 --- a/tests/PhpParser/Printer/PHPStanPrinterTest.php +++ b/tests/PhpParser/Printer/PHPStanPrinterTest.php @@ -7,15 +7,15 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Parser\Parser; use PHPStan\Parser\RichParser; +use Rector\DependencyInjection\PHPStan\PHPStanContainerMemento; use Rector\Testing\PHPUnit\AbstractLazyTestCase; -<<<<<<< HEAD use ReflectionProperty; -======= ->>>>>>> ea9a6ad8a5 ([issue 9492] create reproducer for modified array_map args, as creates changed args on print) /** * Test case for: https://github.com/rectorphp/rector/issues/9492 * Most likely caused by https://github.com/phpstan/phpstan-src/pull/3763 + * + * @see https://github.com/phpstan/phpstan-src/blob/2.1.x/src/Parser/ArrayMapArgVisitor.php */ final class PHPStanPrinterTest extends AbstractLazyTestCase { @@ -24,33 +24,22 @@ public function testAddingCommentOnSomeNodesFail(): void /** @var RichParser $phpstanParser */ $phpstanParser = $this->make(Parser::class); + PHPStanContainerMemento::removeRichVisitors($phpstanParser); + $stmts = $phpstanParser->parseFile(__DIR__ . '/Fixture/some_array_map.php'); // get private property "parser" -<<<<<<< HEAD $parserReflectionProperty = new ReflectionProperty(RichParser::class, 'parser'); -======= - $parserReflectionProperty = new \ReflectionProperty(RichParser::class, 'parser'); ->>>>>>> ea9a6ad8a5 ([issue 9492] create reproducer for modified array_map args, as creates changed args on print) /** @var \PhpParser\Parser $innerParser */ $innerParser = $parserReflectionProperty->getValue($phpstanParser); $tokens = $innerParser->getTokens(); -<<<<<<< HEAD $standard = new Standard([]); $printerContents = $standard->printFormatPreserving($stmts, $stmts, $tokens); $newlineNormalizedContents = str_replace("\r\n", PHP_EOL, $printerContents); $this->assertStringEqualsFile(__DIR__ . '/Fixture/some_array_map.php', $newlineNormalizedContents); -======= - $standardPrinter = new Standard([ - 'newline' => "\n", - ]); - $printerContents = $standardPrinter->printFormatPreserving($stmts, $stmts, $tokens); - - $this->assertStringEqualsFile(__DIR__ . '/Fixture/some_array_map.php', $printerContents); ->>>>>>> ea9a6ad8a5 ([issue 9492] create reproducer for modified array_map args, as creates changed args on print) } } From ac0e705483e2a91e3769a192c1eb406ebeb9dcb1 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 4 Dec 2025 22:28:50 +0100 Subject: [PATCH 3/3] remove docblock based type, as might be invalid --- .../return_by_array_shape_type.php.inc | 33 ------------------- ...wFunctionParamArrayWhereDimFetchRector.php | 4 ++- .../PHPStan/PHPStanContainerMemento.php | 10 ++++-- .../Printer/Fixture/some_array_map.php | 4 +-- 4 files changed, 12 insertions(+), 39 deletions(-) delete mode 100644 rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_by_array_shape_type.php.inc diff --git a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_by_array_shape_type.php.inc b/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_by_array_shape_type.php.inc deleted file mode 100644 index bb3cdad9084..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ArrowFunction/AddArrowFunctionReturnTypeRector/Fixture/return_by_array_shape_type.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - $values - */ - private function foo(array $values): void - { - $bars = array_map(fn($value) => $value['bar'], $values); - } -} - -?> ------ - $values - */ - private function foo(array $values): void - { - $bars = array_map(fn($value): int => $value['bar'], $values); - } -} - -?> diff --git a/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php b/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php index 1fa3c45f010..58d691f303a 100644 --- a/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php +++ b/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php @@ -12,6 +12,7 @@ use PhpParser\Node\Expr\Instanceof_; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; +use PhpParser\Node\Scalar\String_; use PHPStan\Type\ArrayType; use Rector\PhpParser\Node\BetterNodeFinder; use Rector\Rector\AbstractRector; @@ -148,7 +149,8 @@ private function resolveDimFetchVariableNames(Closure|ArrowFunction $closureExpr if ($arrayDimFetch->var instanceof Variable) { $type = $this->nodeTypeResolver->getNativeType($arrayDimFetch->var); - if ($type->isString()->yes()) { + // skip string values + if (! $arrayDimFetch->dim instanceof String_ && ($type->isString()->yes() || $type->isString()->maybe())) { continue; } diff --git a/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php b/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php index 9934a7556db..0edd4491c97 100644 --- a/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php +++ b/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php @@ -8,6 +8,8 @@ use PHPStan\DependencyInjection\Nette\NetteContainer; use PHPStan\Parser\AnonymousClassVisitor; use PHPStan\Parser\RichParser; +use PHPStan\Parser\VariadicFunctionsVisitor; +use PHPStan\Parser\VariadicMethodsVisitor; use Rector\Util\Reflection\PrivatesAccessor; /** @@ -34,12 +36,16 @@ public static function removeRichVisitors(RichParser $richParser): void // get tags property $tags = $privatesAccessor->getPrivateProperty($deeperContainer, 'tags'); - // keep only the anonymous class visitor + // keep visitors that are useful // remove all the rest, https://github.com/phpstan/phpstan-src/tree/1d86de8bb9371534983a8dbcd879e057d2ff028f/src/Parser - $tags[RichParser::VISITOR_SERVICE_TAG] = [ + $nodeVisitorsToKeep = [ $container->findServiceNamesByType(AnonymousClassVisitor::class)[0] => true, + $container->findServiceNamesByType(VariadicFunctionsVisitor::class)[0] => true, + $container->findServiceNamesByType(VariadicMethodsVisitor::class)[0] => true, ]; + $tags[RichParser::VISITOR_SERVICE_TAG] = $nodeVisitorsToKeep; + $privatesAccessor->setPrivateProperty($deeperContainer, 'tags', $tags); } } diff --git a/tests/PhpParser/Printer/Fixture/some_array_map.php b/tests/PhpParser/Printer/Fixture/some_array_map.php index 824ecfc90d8..c5a50902dd6 100644 --- a/tests/PhpParser/Printer/Fixture/some_array_map.php +++ b/tests/PhpParser/Printer/Fixture/some_array_map.php @@ -1,5 +1,3 @@ $value); +$result = \array_map(array: [1, 2, 3], callback: fn(int $value) => $value);