diff --git a/.github/workflows/fatal_scan.yaml b/.github/workflows/fatal_scan.yaml deleted file mode 100644 index 9f2d13eaf2b7..000000000000 --- a/.github/workflows/fatal_scan.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: Fatal Scan - -on: - pull_request: null - push: - branches: - - master - -jobs: - fatal_scan: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: shivammathur/setup-php@v1 - with: - php-version: 7.3 - coverage: none # disable xdebug, pcov - - run: composer install --no-progress --ansi - - run: bin/rector scan-fatal-errors tests/Source/FatalErrors diff --git a/.gitignore b/.gitignore index a08dfd6209fc..73dd31cb1090 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,3 @@ create-rector.yaml /compiler/composer.lock /compiler/vendor /tmp - -# from "scan-fatal-errors" command -rector-types.yaml diff --git a/src/Console/Command/ScanFatalErrorsCommand.php b/src/Console/Command/ScanFatalErrorsCommand.php deleted file mode 100644 index ea431a537848..000000000000 --- a/src/Console/Command/ScanFatalErrorsCommand.php +++ /dev/null @@ -1,107 +0,0 @@ -symfonyStyle = $symfonyStyle; - $this->scannedErrorToRectorResolver = $scannedErrorToRectorResolver; - $this->errorScanner = $errorScanner; - $this->yamlPrinter = $yamlPrinter; - - parent::__construct(); - } - - protected function configure(): void - { - $this->setName(CommandNaming::classToName(self::class)); - - $this->setDescription('Scan for fatal type errors and dumps config that fixes it'); - - $this->addArgument( - Option::SOURCE, - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'Path to file/directory to process' - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - /** @var string[] $source */ - $source = $input->getArgument(Option::SOURCE); - $errors = $this->errorScanner->scanSource($source); - - if ($errors === []) { - $this->symfonyStyle->success('No fatal errors found'); - return ShellCode::SUCCESS; - } - - $this->symfonyStyle->section(sprintf('Found %d Errors', count($errors))); - foreach ($errors as $error) { - $this->symfonyStyle->note($error); - } - - $rectorConfiguration = $this->scannedErrorToRectorResolver->processErrors($errors); - if ($rectorConfiguration === []) { - $this->symfonyStyle->success('No fatal errors found'); - return ShellCode::SUCCESS; - } - - $this->yamlPrinter->printYamlToFile($rectorConfiguration, self::RECTOR_TYPES_YAML); - - $this->symfonyStyle->note(sprintf('New config with types was created in "%s"', self::RECTOR_TYPES_YAML)); - - $this->symfonyStyle->success(sprintf( - 'Now run Rector to refactor your code:%svendor/bin/rector p %s --config %s', - PHP_EOL . PHP_EOL, - implode(' ', $source), - self::RECTOR_TYPES_YAML - )); - - return ShellCode::SUCCESS; - } -} diff --git a/src/Scan/ErrorScanner.php b/src/Scan/ErrorScanner.php deleted file mode 100644 index 3c93376d9feb..000000000000 --- a/src/Scan/ErrorScanner.php +++ /dev/null @@ -1,115 +0,0 @@ -filesFinder = $filesFinder; - $this->symfonyStyle = $symfonyStyle; - } - - /** - * @param string[] $source - * @return string[] - */ - public function scanSource(array $source): array - { - $this->setErrorHandler(); - - $fileInfos = $this->filesFinder->findInDirectoriesAndFiles($source, ['php']); - - foreach ($fileInfos as $fileInfo) { - $currentCommandLine = self::COMMAND_LINE . PHP_EOL; - $currentCommandLine .= sprintf('include "%s";', $fileInfo->getRelativeFilePathFromCwd()); - - $currentCommandLine = sprintf("php -r '%s'", $currentCommandLine); - - $this->symfonyStyle->note('Running PHP in sub-process: ' . $currentCommandLine); - - $process = Process::fromShellCommandline($currentCommandLine); - $process->run(); - - if ($process->isSuccessful()) { - continue; - } - - $this->errors[] = trim($process->getErrorOutput()); - } - - $this->restoreErrorHandler(); - - return $this->errors; - } - - public function shutdown_function(): void - { - $error = error_get_last(); - //check if it's a core/fatal error, otherwise it's a normal shutdown - if ($error === null) { - return; - } - - if (! in_array( - $error['type'], - [ - E_ERROR, - E_PARSE, - E_CORE_ERROR, - E_CORE_WARNING, - E_COMPILE_ERROR, - E_COMPILE_WARNING, - E_RECOVERABLE_ERROR, - ], true - )) { - return; - } - - print_r($error); - } - - /** - * @see https://www.php.net/manual/en/function.set-error-handler.php - * @see https://stackoverflow.com/a/36638910/1348344 - */ - private function setErrorHandler(): void - { - register_shutdown_function([$this, 'shutdown_function']); - - set_error_handler(function (int $num, string $error): void { - $this->errors[] = $error; - }); - } - - private function restoreErrorHandler(): void - { - restore_error_handler(); - } -} diff --git a/src/Scan/ScannedErrorToRectorResolver.php b/src/Scan/ScannedErrorToRectorResolver.php deleted file mode 100644 index b2e1374ec9d9..000000000000 --- a/src/Scan/ScannedErrorToRectorResolver.php +++ /dev/null @@ -1,180 +0,0 @@ -\w.*?) should be compatible with (?\w.*?)$#'; - - /** - * @see https://regex101.com/r/D6Z5Uq/1/ - * @var string - */ - private const INCOMPATIBLE_RETURN_TYPE_PATTERN = '#Declaration of (?\w.*?) must be compatible with (?\w.*?)$#'; - - /** - * @see https://regex101.com/r/RbJy9h/8/ - * @var string - */ - private const CLASS_METHOD_ARGUMENTS_PATTERN = '#(?.*?)::(?.*?)\((?.*?)\)(:\s?(?\w+))?#'; - - /** - * @see https://regex101.com/r/RbJy9h/5 - * @var string - */ - private const ARGUMENTS_PATTERN = '#(\b(?\w.*?)?\b )?\$(?\w+)#sm'; - - /** - * @var mixed[] - */ - private $paramChanges = []; - - /** - * @var mixed[] - */ - private $returnChanges = []; - - /** - * @param string[] $errors - * @return mixed[] - */ - public function processErrors(array $errors): array - { - $this->paramChanges = []; - - foreach ($errors as $fatalError) { - $match = Strings::match($fatalError, self::INCOMPATIBLE_PARAM_TYPE_PATTERN); - if ($match) { - $this->processIncompatibleParamTypeMatch($match); - continue; - } - - $match = Strings::match($fatalError, self::INCOMPATIBLE_RETURN_TYPE_PATTERN); - if ($match) { - $this->processIncompatibleReturnTypeMatch($match); - } - } - - $config = []; - if ($this->paramChanges !== []) { - $config['services'][AddParamTypeDeclarationRector::class]['$typehintForParameterByMethodByClass'] = $this->paramChanges; - } - - if ($this->returnChanges !== []) { - $config['services'][AddReturnTypeDeclarationRector::class]['$typehintForMethodByClass'] = $this->returnChanges; - } - - return $config; - } - - private function processIncompatibleParamTypeMatch(array $match): void - { - if (! Strings::contains($match['current'], '::')) { - // probably a function? - throw new NotImplementedException(); - } - - $scannedMethod = $this->createScannedMethod($match['current']); - $shouldBeMethod = $this->createScannedMethod($match['should_be']); - - $this->collectClassMethodParamDifferences($scannedMethod, $shouldBeMethod); - } - - private function processIncompatibleReturnTypeMatch(array $match): void - { - if (! Strings::contains($match['current'], '::')) { - // probably a function? - throw new NotImplementedException(); - } - - $scannedMethod = $this->createScannedMethod($match['current']); - $shouldBeMethod = $this->createScannedMethod($match['should_be']); - - $this->collectClassMethodReturnDifferences($scannedMethod, $shouldBeMethod); - } - - private function createScannedMethod(string $classMethodWithArgumentsDescription): ClassMethodWithArguments - { - $match = Strings::match($classMethodWithArgumentsDescription, self::CLASS_METHOD_ARGUMENTS_PATTERN); - if (! $match) { - throw new NotImplementedException(); - } - - $arguments = $this->createArguments((string) $match['arguments']); - - return new ClassMethodWithArguments( - $match['class'], - $match['method'], - $arguments, - $match['return_type'] ?? '' - ); - } - - private function collectClassMethodParamDifferences( - ClassMethodWithArguments $scannedMethod, - ClassMethodWithArguments $shouldBeMethod - ): void { - foreach ($scannedMethod->getArguments() as $scannedMethodArgument) { - $shouldBeArgument = $shouldBeMethod->getArgumentByPosition($scannedMethodArgument->getPosition()); - - if ($shouldBeArgument === null) { - throw new NotImplementedException(); - } - - // types are identical, nothing to change - if ($scannedMethodArgument->getType() === $shouldBeArgument->getType()) { - continue; - } - - $this->paramChanges[$scannedMethod->getClass()][$scannedMethod->getMethod()][$scannedMethodArgument->getPosition()] = $shouldBeArgument->getType(); - } - } - - private function collectClassMethodReturnDifferences( - ClassMethodWithArguments $scannedMethod, - ClassMethodWithArguments $shouldBeMethod - ): void { - if ($scannedMethod->getReturnType() === $shouldBeMethod->getReturnType()) { - return; - } - - $this->returnChanges[$scannedMethod->getClass()][$scannedMethod->getMethod()] = $shouldBeMethod->getReturnType(); - } - - /** - * @return Argument[] - */ - private function createArguments(string $argumentsDescription): array - { - // 0 arguments - if ($argumentsDescription === '') { - return []; - } - - $arguments = []; - $argumentDescriptions = Strings::split($argumentsDescription, '#\b,\b#'); - foreach ($argumentDescriptions as $position => $argumentDescription) { - $match = Strings::match((string) $argumentDescription, self::ARGUMENTS_PATTERN); - if (! $match) { - throw new NotImplementedException(); - } - - $arguments[] = new Argument($position, $match['type'] ?? ''); - } - - return $arguments; - } -} diff --git a/src/ValueObject/Scan/Argument.php b/src/ValueObject/Scan/Argument.php deleted file mode 100644 index 1e227225b896..000000000000 --- a/src/ValueObject/Scan/Argument.php +++ /dev/null @@ -1,34 +0,0 @@ -position = $position; - $this->type = $type; - } - - public function getPosition(): int - { - return $this->position; - } - - public function getType(): string - { - return $this->type; - } -} diff --git a/src/ValueObject/Scan/ClassMethodWithArguments.php b/src/ValueObject/Scan/ClassMethodWithArguments.php deleted file mode 100644 index 503a8318ed33..000000000000 --- a/src/ValueObject/Scan/ClassMethodWithArguments.php +++ /dev/null @@ -1,75 +0,0 @@ -class = $class; - $this->method = $method; - $this->arguments = $arguments; - $this->returnType = $returnType; - } - - public function getClass(): string - { - return $this->class; - } - - public function getMethod(): string - { - return $this->method; - } - - /** - * @return Argument[] - */ - public function getArguments(): array - { - return $this->arguments; - } - - public function getArgumentByPosition(int $position): ?Argument - { - foreach ($this->arguments as $argument) { - if ($argument->getPosition() !== $position) { - continue; - } - - return $argument; - } - - return null; - } - - public function getReturnType(): string - { - return $this->returnType; - } -} diff --git a/src/Yaml/YamlPrinter.php b/src/Yaml/YamlPrinter.php index bbab1f4e6567..439fc15ad15d 100644 --- a/src/Yaml/YamlPrinter.php +++ b/src/Yaml/YamlPrinter.php @@ -4,7 +4,6 @@ namespace Rector\Core\Yaml; -use Nette\Utils\FileSystem; use Symfony\Component\Yaml\Yaml; final class YamlPrinter @@ -13,10 +12,4 @@ public function printYamlToString(array $yaml): string { return Yaml::dump($yaml, 10, 4, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK); } - - public function printYamlToFile(array $yaml, string $targetFile): void - { - $yamlContent = $this->printYamlToString($yaml); - FileSystem::write($targetFile, $yamlContent); - } } diff --git a/tests/Scan/ScannedErrorToRectorResolverTest.php b/tests/Scan/ScannedErrorToRectorResolverTest.php deleted file mode 100644 index dd74dad4f6bc..000000000000 --- a/tests/Scan/ScannedErrorToRectorResolverTest.php +++ /dev/null @@ -1,72 +0,0 @@ -bootKernel(RectorKernel::class); - - $this->scannedErrorToRectorResolver = self::$container->get(ScannedErrorToRectorResolver::class); - } - - public function testParam(): void - { - $errors = []; - $errors[] = 'Declaration of Kedlubna\extendTest::add($message) should be compatible with Kedlubna\test::add(string $message = \'\')'; - - $rectorConfiguration = $this->scannedErrorToRectorResolver->processErrors($errors); - - $expectedConfiguration = [ - 'services' => [ - AddParamTypeDeclarationRector::class => [ - '$typehintForParameterByMethodByClass' => [ - 'Kedlubna\extendTest' => [ - 'add' => [ - 0 => 'string', - ], - ], - ], - ], - ], - ]; - - $this->assertSame($expectedConfiguration, $rectorConfiguration); - } - - public function testReturn(): void - { - $errors = []; - $errors[] = 'Declaration of AAA\extendTest::nothing() must be compatible with AAA\test::nothing(): void;'; - - $rectorConfiguration = $this->scannedErrorToRectorResolver->processErrors($errors); - - $expectedConfiguration = [ - 'services' => [ - AddReturnTypeDeclarationRector::class => [ - '$typehintForMethodByClass' => [ - 'AAA\extendTest' => [ - 'nothing' => 'void', - ], - ], - ], - ], - ]; - - $this->assertSame($expectedConfiguration, $rectorConfiguration); - } -}