Make progress bar reflect number of processed files + process file by file in series (#362)

* use file-native progress bar count

* singularize file processor due to changed process order

* try PR in typo3 with contract update

* improve package tests

* cleanup
This commit is contained in:
Tomas Votruba 2021-07-02 22:45:25 +02:00 committed by GitHub
parent 9b92c6ee0c
commit 2a0cae5507
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 188 additions and 199 deletions

View File

@ -37,8 +37,7 @@ jobs:
# early downgrade individual functions and files of symfony Attribute classes
- run: bin/rector process src/functions -c build/config/config-downgrade.php --ansi
- run: bin/rector process vendor/symfony/dependency-injection/Attribute/Autoconfigure.php -c build/config/config-downgrade.php --ansi
- run: bin/rector process vendor/symfony/dependency-injection/Attribute/AutoconfigureTag.php -c build/config/config-downgrade.php --ansi
- run: bin/rector process vendor/symfony/dependency-injection/Attribute -c build/config/config-downgrade.php --ansi
# 1. copy files to $NESTED_DIRECTORY directory Exclude the scoped/nested directories to prevent rsync from copying in a loop
- run: rsync --exclude rector-build -av * rector-build --quiet

View File

@ -43,6 +43,18 @@ jobs:
php-version: 8.0
coverage: none
- run: composer config minimum-stability dev
# test with current commit in a pull-request
-
run: composer require rector/rector-src dev-main#${{github.event.pull_request.head.sha}} --no-install
if: ${{ github.event_name == 'pull_request' }}
# temporary - remove after typo3-rector gets tagged
-
run: composer require ssch/typo3-rector dev-main --no-install
if: ${{ github.event_name == 'pull_request' }}
- run: composer install --ansi
- run: vendor/bin/phpunit

View File

@ -26,11 +26,11 @@
"rector/rector-cakephp": "^0.11.3",
"rector/rector-doctrine": "^0.11.6",
"rector/rector-laravel": "^0.11.2",
"rector/rector-nette": "^0.11.9",
"rector/rector-nette": "^0.11.14",
"rector/rector-phpunit": "^0.11.3",
"rector/rector-symfony": "^0.11.7",
"sebastian/diff": "^4.0.4",
"ssch/typo3-rector": "^0.11.14",
"ssch/typo3-rector": "dev-main",
"symfony/console": "^5.3",
"symfony/dependency-injection": "^5.3",
"symfony/finder": "^5.3",

View File

@ -67,6 +67,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
__DIR__ . '/../src/HttpKernel',
__DIR__ . '/../src/ValueObject',
__DIR__ . '/../src/Bootstrap',
__DIR__ . '/../src/Enum',
__DIR__ . '/../src/PhpParser/Node/CustomNode',
__DIR__ . '/../src/functions',
__DIR__ . '/../src/constants.php',

View File

@ -173,6 +173,15 @@ final class PhpDocInfo
return $this->getTypeOrMixed($this->getReturnTagValue());
}
/**
* @param class-string<TNode> $type
* @return TNode[]
*/
public function getByType(string $type): array
{
return $this->phpDocNodeByTypeFinder->findByType($this->phpDocNode, $type);
}
/**
* @param class-string<TNode> $type
*/

View File

@ -96,8 +96,10 @@ final class StaticCallMethodCallTypeResolver implements NodeTypeResolverInterfac
$methodReflection = $ancestorClassReflection->getMethod($methodName, $scope);
if ($methodReflection instanceof PhpMethodReflection) {
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());
return $parametersAcceptor->getReturnType();
$parametersAcceptorWithPhpDocs = ParametersAcceptorSelector::selectSingle(
$methodReflection->getVariants()
);
return $parametersAcceptorWithPhpDocs->getReturnType();
}
}

View File

@ -536,3 +536,10 @@ parameters:
- '#Cognitive complexity for "Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\:\:resolveNestedDoctrineTagValueNodes\(\)" is 11, keep it under 9#'
- '#expects class\-string<TNode of PHPStan\\PhpDocParser\\Ast\\Node\>, string given#'
# weird generics
- '#Method Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\:\:getByType\(\) should return array<TNode of PHPStan\\PhpDocParser\\Ast\\Node\> but returns array<PHPStan\\PhpDocParser\\Ast\\Node\>#'
# should be allowed, as continue check - fix in symplify
-
message: '#foreach\(\.\.\.\), while\(\), for\(\) or if\(\.\.\.\) cannot contains a complex expression\. Extract it to a new variable assign on line before#'
path: src/Application/ApplicationFileProcessor.php

View File

@ -25,18 +25,28 @@ final class ComposerFileProcessor implements FileProcessorInterface
) {
}
/**
* @param File[] $files
*/
public function process(array $files, Configuration $configuration): void
public function process(File $file, Configuration $configuration): void
{
if ($this->composerRectors === []) {
return;
}
foreach ($files as $file) {
$this->processFile($file);
// to avoid modification of file
$smartFileInfo = $file->getSmartFileInfo();
$composerJson = $this->composerJsonFactory->createFromFileInfo($smartFileInfo);
$oldComposerJson = clone $composerJson;
foreach ($this->composerRectors as $composerRector) {
$composerRector->refactor($composerJson);
}
// nothing has changed
if ($oldComposerJson->getJsonArray() === $composerJson->getJsonArray()) {
return;
}
$changeFileContent = $this->composerJsonPrinter->printToString($composerJson);
$file->changeFileContent($changeFileContent);
}
public function supports(File $file, Configuration $configuration): bool
@ -58,27 +68,6 @@ final class ComposerFileProcessor implements FileProcessorInterface
return ['json'];
}
private function processFile(File $file): void
{
// to avoid modification of file
$smartFileInfo = $file->getSmartFileInfo();
$composerJson = $this->composerJsonFactory->createFromFileInfo($smartFileInfo);
$oldComposerJson = clone $composerJson;
foreach ($this->composerRectors as $composerRector) {
$composerRector->refactor($composerJson);
}
// nothing has changed
if ($oldComposerJson->getJsonArray() === $composerJson->getJsonArray()) {
return;
}
$changeFileContent = $this->composerJsonPrinter->printToString($composerJson);
$file->changeFileContent($changeFileContent);
}
private function isJsonInTests(SmartFileInfo $fileInfo): bool
{
if (! StaticPHPUnitEnvironment::isPHPUnitRun()) {

View File

@ -10,6 +10,7 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Param;
@ -23,6 +24,7 @@ use PhpParser\Node\Stmt\Return_;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\MethodName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -141,9 +143,11 @@ CODE_SAMPLE
*/
private function createAnonymousEventClassBody(): array
{
$return = new Return_(new PropertyFetch(new Variable('this'), 'name'));
return [
new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty(self::NAME)]),
new ClassMethod('__construct', [
new ClassMethod(MethodName::CONSTRUCT, [
'flags' => Class_::MODIFIER_PUBLIC,
'params' => $this->createConstructParams(),
self::STMTS => [new Expression($this->createConstructAssign())],
@ -151,7 +155,7 @@ CODE_SAMPLE
new ClassMethod('eventName', [
'flags' => Class_::MODIFIER_PUBLIC,
'returnType' => 'string',
self::STMTS => [new Return_(new Variable('this->name'))],
self::STMTS => [$return],
]),
];
}
@ -166,6 +170,7 @@ CODE_SAMPLE
private function createConstructAssign(): Assign
{
return new Assign(new Variable('this->name'), new Variable(self::NAME));
$propertyFetch = new PropertyFetch(new Variable('this'), 'name');
return new Assign($propertyFetch, new Variable(self::NAME));
}
}

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Rector\PHPOffice\ValueObject;
use Rector\Core\ValueObject\MethodName;
final class PHPExcelMethodDefaultValues
{
/**
@ -109,7 +111,7 @@ final class PHPExcelMethodDefaultValues
'setDataValues' => [[], true],
],
'PHPExcel_Chart_Layout' => [
'__construct' => [[]],
MethodName::CONSTRUCT => [[]],
],
'PHPExcel_Chart_Legend' => [
'setPosition' => ['PHPExcel_Chart_Legend::POSITION_RIGHT'],
@ -117,7 +119,7 @@ final class PHPExcelMethodDefaultValues
'setOverlay' => [false],
],
'PHPExcel_Chart_PlotArea' => [
'__construct' => [null, []],
MethodName::CONSTRUCT => [null, []],
'setPlotSeries' => [[]],
],
'PHPExcel_Chart_Title' => [

View File

@ -42,7 +42,7 @@ final class AnnotationToAttributeRector extends AbstractRector implements Config
/**
* List of annotations that should not be unwrapped
* @var class-string[]
* @var string[]
*/
private const SKIP_UNWRAP_ANNOTATIONS = [
'Symfony\Component\Validator\Constraints\All',

View File

@ -10,6 +10,7 @@ use Rector\Core\Contract\Processor\FileProcessorInterface;
use Rector\Core\ValueObject\Application\File;
use Rector\Core\ValueObject\Configuration;
use Rector\FileFormatter\FileFormatter;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\SmartFileSystem\SmartFileSystem;
final class ApplicationFileProcessor
@ -22,6 +23,7 @@ final class ApplicationFileProcessor
private FileDiffFileDecorator $fileDiffFileDecorator,
private FileFormatter $fileFormatter,
private RemovedAndAddedFilesProcessor $removedAndAddedFilesProcessor,
private SymfonyStyle $symfonyStyle,
private array $fileProcessors = []
) {
}
@ -43,13 +45,24 @@ final class ApplicationFileProcessor
*/
private function processFiles(array $files, Configuration $configuration): void
{
foreach ($this->fileProcessors as $fileProcessor) {
$supportedFiles = array_filter(
$files,
fn (File $file): bool => $fileProcessor->supports($file, $configuration)
);
if ($configuration->shouldShowDiffs()) {
$fileCount = count($files);
$this->symfonyStyle->progressStart($fileCount);
}
$fileProcessor->process($supportedFiles, $configuration);
foreach ($files as $file) {
foreach ($this->fileProcessors as $fileProcessor) {
if (! $fileProcessor->supports($file, $configuration)) {
continue;
}
$fileProcessor->process($file, $configuration);
}
// progress bar +1
if ($configuration->shouldShowProgressBar()) {
$this->symfonyStyle->progressAdvance();
}
}
$this->removedAndAddedFilesProcessor->run($configuration);

View File

@ -10,6 +10,7 @@ use Rector\Core\Application\FileDecorator\FileDiffFileDecorator;
use Rector\Core\Application\FileProcessor;
use Rector\Core\Application\FileSystem\RemovedAndAddedFilesCollector;
use Rector\Core\Contract\Processor\FileProcessorInterface;
use Rector\Core\Enum\ApplicationPhase;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Printer\FormatPerservingPrinter;
use Rector\Core\Provider\CurrentFileProvider;
@ -18,25 +19,11 @@ use Rector\Core\ValueObject\Application\RectorError;
use Rector\Core\ValueObject\Configuration;
use Rector\PostRector\Application\PostFileProcessor;
use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
use Throwable;
final class PhpFileProcessor implements FileProcessorInterface
{
/**
* Why 4? One for each cycle, so user sees some activity all the time:
*
* 1) parsing files
* 2) main rectoring
* 3) post-rectoring (removing files, importing names)
* 4) printing
*
* @var int
*/
private const PROGRESS_BAR_STEP_MULTIPLIER = 4;
/**
* @var File[]
*/
@ -47,7 +34,6 @@ final class PhpFileProcessor implements FileProcessorInterface
private FileProcessor $fileProcessor,
private RemovedAndAddedFilesCollector $removedAndAddedFilesCollector,
private SymfonyStyle $symfonyStyle,
private PrivatesAccessor $privatesAccessor,
private FileDiffFileDecorator $fileDiffFileDecorator,
private CurrentFileProvider $currentFileProvider,
private PostFileProcessor $postFileProcessor,
@ -55,57 +41,36 @@ final class PhpFileProcessor implements FileProcessorInterface
) {
}
/**
* @param File[] $files
*/
public function process(array $files, Configuration $configuration): void
public function process(File $file, Configuration $configuration): void
{
$fileCount = count($files);
if ($fileCount === 0) {
// 1. parse files to nodes
$this->tryCatchWrapper($file, function (File $file): void {
$this->fileProcessor->parseFileInfoToLocalCache($file);
}, ApplicationPhase::PARSING());
// 2. change nodes with Rectors
$this->refactorNodesWithRectors($file);
// 3. apply post rectors
$this->tryCatchWrapper($file, function (File $file): void {
$newStmts = $this->postFileProcessor->traverse($file->getNewStmts());
// this is needed for new tokens added in "afterTraverse()"
$file->changeNewStmts($newStmts);
}, ApplicationPhase::POST_RECTORS());
// 4. print to file or string
$this->currentFileProvider->setFile($file);
if ($file->hasErrors()) {
// cannot print file with errors, as print would b
$this->notifyPhase($file, ApplicationPhase::PRINT_SKIP());
return;
}
$this->prepareProgressBar($fileCount, $configuration);
// 1. parse files to nodes
foreach ($files as $file) {
$this->tryCatchWrapper($file, function (File $file): void {
$this->fileProcessor->parseFileInfoToLocalCache($file);
}, 'parsing', $configuration);
}
// 2. change nodes with Rectors
$this->refactorNodesWithRectors($files, $configuration);
// 3. apply post rectors
foreach ($files as $file) {
$this->tryCatchWrapper($file, function (File $file): void {
$newStmts = $this->postFileProcessor->traverse($file->getNewStmts());
// this is needed for new tokens added in "afterTraverse()"
$file->changeNewStmts($newStmts);
}, 'post rectors', $configuration);
}
// 4. print to file or string
foreach ($files as $file) {
$this->currentFileProvider->setFile($file);
// cannot print file with errors, as print would break everything to original nodes
if ($file->hasErrors()) {
$this->printFileErrors($file);
$this->advance($file, 'printing skipped due error', $configuration);
continue;
}
$this->tryCatchWrapper($file, function (File $file) use ($configuration): void {
$this->printFile($file, $configuration);
}, 'printing', $configuration);
}
if ($configuration->shouldShowProgressBar()) {
$this->symfonyStyle->newLine(2);
}
$this->tryCatchWrapper($file, function (File $file) use ($configuration): void {
$this->printFile($file, $configuration);
}, ApplicationPhase::PRINT());
}
public function supports(File $file, Configuration $configuration): bool
@ -122,38 +87,19 @@ final class PhpFileProcessor implements FileProcessorInterface
return ['php'];
}
private function prepareProgressBar(int $fileCount, Configuration $configuration): void
{
if ($this->symfonyStyle->isVerbose()) {
return;
}
if (! $configuration->shouldShowProgressBar()) {
return;
}
$this->configureStepCount($fileCount);
}
/**
* @param File[] $files
*/
private function refactorNodesWithRectors(array $files, Configuration $configuration): void
{
foreach ($files as $file) {
$this->currentFileProvider->setFile($file);
$this->tryCatchWrapper($file, function (File $file): void {
$this->fileProcessor->refactor($file);
}, 'refactoring', $configuration);
}
}
private function tryCatchWrapper(File $file, callable $callback, string $phase, Configuration $configuration): void
private function refactorNodesWithRectors(File $file): void
{
$this->currentFileProvider->setFile($file);
$this->advance($file, $phase, $configuration);
$this->tryCatchWrapper($file, function (File $file): void {
$this->fileProcessor->refactor($file);
}, ApplicationPhase::REFACTORING());
}
private function tryCatchWrapper(File $file, callable $callback, ApplicationPhase $applicationPhase): void
{
$this->currentFileProvider->setFile($file);
$this->notifyPhase($file, $applicationPhase);
try {
if (in_array($file, $this->notParsedFiles, true)) {
@ -199,42 +145,15 @@ final class PhpFileProcessor implements FileProcessorInterface
$this->fileDiffFileDecorator->decorate([$file]);
}
/**
* This prevent CI report flood with 1 file = 1 line in progress bar
*/
private function configureStepCount(int $fileCount): void
private function notifyPhase(File $file, ApplicationPhase $applicationPhase): void
{
$this->symfonyStyle->progressStart($fileCount * self::PROGRESS_BAR_STEP_MULTIPLIER);
$progressBar = $this->privatesAccessor->getPrivateProperty($this->symfonyStyle, 'progressBar');
if (! $progressBar instanceof ProgressBar) {
throw new ShouldNotHappenException();
}
if ($progressBar->getMaxSteps() < 40) {
if (! $this->symfonyStyle->isVerbose()) {
return;
}
$redrawFrequency = (int) ($progressBar->getMaxSteps() / 20);
$progressBar->setRedrawFrequency($redrawFrequency);
}
private function advance(File $file, string $phase, Configuration $configuration): void
{
if ($this->symfonyStyle->isVerbose()) {
$smartFileInfo = $file->getSmartFileInfo();
$relativeFilePath = $smartFileInfo->getRelativeFilePathFromDirectory(getcwd());
$message = sprintf('[%s] %s', $phase, $relativeFilePath);
$this->symfonyStyle->writeln($message);
} elseif ($configuration->shouldShowProgressBar()) {
$this->symfonyStyle->progressAdvance();
}
}
private function printFileErrors(File $file): void
{
foreach ($file->getErrors() as $rectorError) {
$this->symfonyStyle->error($rectorError->getMessage());
}
$smartFileInfo = $file->getSmartFileInfo();
$relativeFilePath = $smartFileInfo->getRelativeFilePathFromDirectory(getcwd());
$message = sprintf('[%s] %s', $applicationPhase, $relativeFilePath);
$this->symfonyStyle->writeln($message);
}
}

View File

@ -7,12 +7,14 @@ namespace Rector\Core\Configuration;
use Rector\ChangesReporting\Output\ConsoleOutputFormatter;
use Rector\Core\ValueObject\Configuration;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
final class ConfigurationFactory
{
public function __construct(
private ParameterProvider $parameterProvider
private ParameterProvider $parameterProvider,
private SymfonyStyle $symfonyStyle
) {
}
@ -58,6 +60,10 @@ final class ConfigurationFactory
return false;
}
if ($this->symfonyStyle->isVerbose()) {
return false;
}
return $outputFormat === ConsoleOutputFormatter::NAME;
}

View File

@ -9,11 +9,9 @@ use Rector\Core\ValueObject\Configuration;
interface FileProcessorInterface
{
// @todo wait for implementers to adapt with 2nd parameters of Configuration $configuration
// public function supports(File $file, Configuration $configuration): bool;
public function supports(File $file, Configuration $configuration): bool;
// public function process(array $files, Configuration $configuration): void;
public function process(File $file, Configuration $configuration): void;
/**
* @return string[]

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Enum;
use MyCLabs\Enum\Enum;
/**
* @method static ApplicationPhase REFACTORING()
* @method static ApplicationPhase PRINT_SKIP()
* @method static ApplicationPhase PRINT()
* @method static ApplicationPhase POST_RECTORS()
* @method static ApplicationPhase PARSING()
*/
final class ApplicationPhase extends Enum
{
/**
* @var string
*/
private const REFACTORING = 'refactoring';
/**
* @var string
*/
private const PRINT_SKIP = 'printing skipped due error';
/**
* @var string
*/
private const PRINT = 'print';
/**
* @var string
*/
private const POST_RECTORS = 'post rectors';
/**
* @var string
*/
private const PARSING = 'parsing';
}

View File

@ -23,13 +23,11 @@ final class NonPhpFileProcessor implements FileProcessorInterface
) {
}
/**
* @param File[] $files
*/
public function process(array $files, Configuration $configuration): void
public function process(File $file, Configuration $configuration): void
{
foreach ($files as $file) {
$this->processFile($file);
foreach ($this->nonPhpRectors as $nonPhpRector) {
$newFileContent = $nonPhpRector->refactorFileContent($file->getFileContent());
$file->changeFileContent($newFileContent);
}
}
@ -57,12 +55,4 @@ final class NonPhpFileProcessor implements FileProcessorInterface
{
return StaticNonPhpFileSuffixes::SUFFIXES;
}
private function processFile(File $file): void
{
foreach ($this->nonPhpRectors as $nonPhpRector) {
$newFileContent = $nonPhpRector->refactorFileContent($file->getFileContent());
$file->changeFileContent($newFileContent);
}
}
}

View File

@ -24,20 +24,15 @@ final class TextFileProcessor implements FileProcessorInterface
$this->textRectors = $textRectors;
}
/**
* @param File[] $files
*/
public function process(array $files, Configuration $configuration): void
public function process(File $file, Configuration $configuration): void
{
foreach ($files as $file) {
$fileContent = $file->getFileContent();
$fileContent = $file->getFileContent();
foreach ($this->textRectors as $textRector) {
$fileContent = $textRector->refactorContent($fileContent);
}
$file->changeFileContent($fileContent);
foreach ($this->textRectors as $textRector) {
$fileContent = $textRector->refactorContent($fileContent);
}
$file->changeFileContent($fileContent);
}
public function supports(File $file, Configuration $configuration): bool