From ff546dcd06a10a27b7dc59267b3c143d4018661f Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 10 Oct 2026 00:31:31 +0200 Subject: [PATCH] simplify hacking of RectorParser --- src/Application/FileProcessor.php | 9 ++--- src/PhpParser/Parser/RectorParser.php | 47 +++++++++++++++------------ 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/Application/FileProcessor.php b/src/Application/FileProcessor.php index 1fc05375392..8465c37b17d 100644 --- a/src/Application/FileProcessor.php +++ b/src/Application/FileProcessor.php @@ -111,11 +111,7 @@ public function processFile(File $file, Configuration $configuration): FileProce private function parseFileAndDecorateNodes(File $file): ?SystemError { try { - try { - $this->parseFileNodes($file); - } catch (ParserErrorsException) { - $this->parseFileNodes($file, false); - } + $this->parseFileNodes($file); } catch (ShouldNotHappenException $shouldNotHappenException) { throw $shouldNotHappenException; } catch (AnalysedCodeException $analysedCodeException) { @@ -164,12 +160,11 @@ private function printFile(File $file, Configuration $configuration, string $fil FileSystem::write($filePath, $newFileContent, null); } - private function parseFileNodes(File $file, bool $forNewestSupportedVersion = true): void + private function parseFileNodes(File $file): void { // store tokens by original file content, so we don't have to print them right now $stmtsAndTokens = $this->rectorParser->parseFileContentToStmtsAndTokens( $file->getOriginalFileContent(), - $forNewestSupportedVersion ); $oldStmts = $stmtsAndTokens->getStmts(); diff --git a/src/PhpParser/Parser/RectorParser.php b/src/PhpParser/Parser/RectorParser.php index e9479e590fd..788ec0760ff 100644 --- a/src/PhpParser/Parser/RectorParser.php +++ b/src/PhpParser/Parser/RectorParser.php @@ -9,8 +9,10 @@ use PhpParser\PhpVersion; use PHPStan\Parser\Parser; use PHPStan\Parser\RichParser; +use Rector\Php\PhpVersionProvider; use Rector\PhpParser\ValueObject\StmtsAndTokens; use Rector\Util\Reflection\PrivatesAccessor; +use Rector\ValueObject\PhpVersion as CorePhpVersion; final readonly class RectorParser { @@ -19,7 +21,8 @@ */ public function __construct( private Parser $parser, - private PrivatesAccessor $privatesAccessor + private PrivatesAccessor $privatesAccessor, + private PhpVersionProvider $phpVersionProvider ) { } @@ -41,32 +44,36 @@ public function parseString(string $fileContent): array return $this->parser->parseString($fileContent); } - public function parseFileContentToStmtsAndTokens( - string $fileContent, - bool $forNewestSupportedVersion = true - ): StmtsAndTokens { - if (! $forNewestSupportedVersion) { - // don't directly change PHPStan Parser service - // to avoid reuse on next file - $phpstanParser = clone $this->parser; + public function parseFileContentToStmtsAndTokens(string $fileContent): StmtsAndTokens + { + $parser = $this->resolveConfiguredVersionParser(); - $parserFactory = new ParserFactory(); - $parser = $parserFactory->createForVersion(PhpVersion::fromString('7.0')); - $this->privatesAccessor->setPrivateProperty($phpstanParser, 'parser', $parser); + $stmts = $parser->parseString($fileContent); - return $this->resolveStmtsAndTokens($phpstanParser, $fileContent); - } + $innerParser = $this->privatesAccessor->getPrivateProperty($parser, 'parser'); + $tokens = $innerParser->getTokens(); - return $this->resolveStmtsAndTokens($this->parser, $fileContent); + return new StmtsAndTokens($stmts, $tokens); } - private function resolveStmtsAndTokens(Parser $parser, string $fileContent): StmtsAndTokens + private function resolveConfiguredVersionParser(): Parser { - $stmts = $parser->parseString($fileContent); + $phpVersion = $this->phpVersionProvider->provide(); - $innerParser = $this->privatesAccessor->getPrivateProperty($parser, 'parser'); - $tokens = $innerParser->getTokens(); + // on 8.0+ the default newest parser is correct; + // only pre-8.0 syntax (e.g. curly offset access) needs an older parser + if ($phpVersion >= CorePhpVersion::PHP_80) { + return $this->parser; + } - return new StmtsAndTokens($stmts, $tokens); + // don't mutate the shared PHPStan parser service, clone to avoid reuse on the next file + $phpstanParser = clone $this->parser; + + $parser = new ParserFactory()->createForVersion( + PhpVersion::fromComponents(intdiv($phpVersion, 10000), intdiv($phpVersion % 10000, 100)) + ); + $this->privatesAccessor->setPrivateProperty($phpstanParser, 'parser', $parser); + + return $phpstanParser; } }