fix StringFormTypeToClassRector

This commit is contained in:
Tomas Votruba 2018-09-21 01:19:56 +02:00
parent 0f2f74ca8a
commit 5c3067c95d
14 changed files with 155 additions and 46 deletions

View File

@ -46,8 +46,6 @@ final class DocBlockAnalyzer
}
return Strings::contains($node->getDocComment()->getText(), '@' . $name);
// $phpDocInfo = $this->createPhpDocInfoFromNode($node);
// return $phpDocInfo->hasTag($name);
}
public function removeParamTagByName(Node $node, string $name): void

View File

@ -20,9 +20,9 @@ final class ExceptionAnnotationRectorTest extends AbstractRectorTestCase
public function provideWrongToFixedFiles(): Iterator
{
// yield [__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'];
// yield [__DIR__ . '/Wrong/wrong2.php.inc', __DIR__ . '/Correct/correct2.php.inc'];
// yield [__DIR__ . '/Wrong/wrong3.php.inc', __DIR__ . '/Correct/correct3.php.inc'];
yield [__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'];
yield [__DIR__ . '/Wrong/wrong2.php.inc', __DIR__ . '/Correct/correct2.php.inc'];
yield [__DIR__ . '/Wrong/wrong3.php.inc', __DIR__ . '/Correct/correct3.php.inc'];
yield [__DIR__ . '/Wrong/wrong4.php.inc', __DIR__ . '/Correct/correct4.php.inc'];
}

View File

@ -45,6 +45,10 @@ final class FormTypeStringToTypeProvider
public function hasClassForNameWithPrefix(string $name): bool
{
if (! Strings::startsWith($name, 'form.type.')) {
return false;
}
$name = $this->removeFormTypePrefix($name);
return $this->hasClassForName($name);

View File

@ -3,9 +3,10 @@
namespace Rector\Symfony\Rector\Form;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Scalar\String_;
use Rector\Node\NodeFactory;
use Rector\NodeTypeResolver\Node\Attribute;
use Rector\NodeAnalyzer\MethodCallAnalyzer;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@ -26,9 +27,25 @@ final class StringFormTypeToClassRector extends AbstractRector
*/
private $formTypeStringToTypeProvider;
public function __construct(NodeFactory $nodeFactory, FormTypeStringToTypeProvider $formTypeStringToTypeProvider)
{
/**
* @var string
*/
private $formBuilderClass;
/**
* @var MethodCallAnalyzer
*/
private $methodCallAnalyzer;
public function __construct(
NodeFactory $nodeFactory,
FormTypeStringToTypeProvider $formTypeStringToTypeProvider,
MethodCallAnalyzer $methodCallAnalyzer,
string $formBuilderClass = 'Symfony\Component\Form\FormBuilderInterface'
) {
$this->nodeFactory = $nodeFactory;
$this->formBuilderClass = $formBuilderClass;
$this->methodCallAnalyzer = $methodCallAnalyzer;
$this->formTypeStringToTypeProvider = $formTypeStringToTypeProvider;
}
@ -41,8 +58,15 @@ final class StringFormTypeToClassRector extends AbstractRector
'Turns string Form Type references to their CONSTANT alternatives in FormTypes in Form in Symfony',
[
new CodeSample(
'$form->add("name", "form.type.text");',
'$form->add("name", \Symfony\Component\Form\Extension\Core\Type\TextType::class);'
<<<'CODE_SAMPLE'
$formBuilder = new Symfony\Component\Form\FormBuilder;
$formBuilder->add('name', 'form.type.text');
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
$formBuilder = new Symfony\Component\Form\FormBuilder;
$form->add('name', \Symfony\Component\Form\Extension\Core\Type\TextType::class);
CODE_SAMPLE
),
]
);
@ -53,22 +77,40 @@ final class StringFormTypeToClassRector extends AbstractRector
*/
public function getNodeTypes(): array
{
return [String_::class];
return [MethodCall::class];
}
/**
* @param String_ $stringNode
* @param MethodCall $methodCallNode
*/
public function refactor(Node $stringNode): ?Node
public function refactor(Node $methodCallNode): ?Node
{
if (! $this->methodCallAnalyzer->isTypeAndMethod($methodCallNode, $this->formBuilderClass, 'add')) {
return null;
}
// just one argument
if (! isset($methodCallNode->args[1])) {
return null;
}
// not a string
if (! $methodCallNode->args[1]->value instanceof String_) {
return null;
}
/** @var String_ $stringNode */
$stringNode = $methodCallNode->args[1]->value;
// not a form type string
if (! $this->formTypeStringToTypeProvider->hasClassForNameWithPrefix($stringNode->value)) {
return null;
}
if (((string) $stringNode->getAttribute(Attribute::METHOD_CALL_NAME) === 'add') === false) {
return null;
}
$class = $this->formTypeStringToTypeProvider->getClassForNameWithPrefix($stringNode->value);
return $this->nodeFactory->createClassConstantReference($class);
$methodCallNode->args[1]->value = $this->nodeFactory->createClassConstantReference($class);
return $methodCallNode;
}
}

View File

@ -1,6 +1,8 @@
<?php declare (strict_types=1);
$formBuilder = new Symfony\Component\Form\FormBuilder;
use Rector\Symfony\Tests\Rector\Form\StringFormTypeToClassRector\Source\FormBuilder;
$formBuilder = new FormBuilder();
$formBuilder->add('task', \Symfony\Component\Form\Extension\Core\Type\TextType::class);
$variable = 'form.type.text';

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Form\Type;
use Rector\Symfony\Tests\Rector\Form\StringFormTypeToClassRector\Source\FormBuilder;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
final class ContactType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilder $builder, array $options): void
{
$builder
->add('email', EmailType::class, [
'label' => 'sylius.ui.email',
])
;
}
}

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace Rector\Symfony\Tests\Rector\Form\StringFormTypeToClassRector\Source;
final class FormBuilder
{
}

View File

@ -21,6 +21,7 @@ final class StringFormTypeToClassRectorTest extends AbstractRectorTestCase
public function provideWrongToFixedFiles(): Iterator
{
yield [__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'];
yield [__DIR__ . '/Wrong/wrong2.php.inc', __DIR__ . '/Correct/correct2.php.inc'];
}
protected function provideConfig(): string

View File

@ -1,6 +1,8 @@
<?php declare (strict_types=1);
$formBuilder = new Symfony\Component\Form\FormBuilder;
use Rector\Symfony\Tests\Rector\Form\StringFormTypeToClassRector\Source\FormBuilder;
$formBuilder = new FormBuilder();
$formBuilder->add('task', 'form.type.text');
$variable = 'form.type.text';

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Form\Type;
use Rector\Symfony\Tests\Rector\Form\StringFormTypeToClassRector\Source\FormBuilder;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
final class ContactType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilder $builder, array $options): void
{
$builder
->add('email', EmailType::class, [
'label' => 'sylius.ui.email',
])
;
}
}

View File

@ -1,2 +1,3 @@
services:
Rector\Symfony\Rector\Form\StringFormTypeToClassRector: ~
Rector\Symfony\Rector\Form\StringFormTypeToClassRector:
$formBuilderClass: 'Rector\Symfony\Tests\Rector\Form\StringFormTypeToClassRector\Source\FormBuilder'

View File

@ -7,6 +7,7 @@ use Rector\Configuration\Option;
use Rector\FileSystem\FileGuard;
use Rector\Utils\FilesystemTweaker;
use Symfony\Component\Console\Input\InputInterface;
use Throwable;
/**
* Should it pass autoload files/directories to PHPStan analyzer?
@ -110,7 +111,7 @@ final class AdditionalAutoloader
// sometimes tests can include ambiguous classes
try {
$robotLoader->register();
} catch (\Throwable $throwable) {
} catch (Throwable $throwable) {
}
}

View File

@ -200,30 +200,7 @@ final class ProcessCommand extends Command
$this->consoleStyle->progressStart($totalFiles);
foreach ($fileInfos as $fileInfo) {
try {
// php
if ($fileInfo->getExtension() === 'php') {
$this->processFile($fileInfo);
// yml
} elseif ($fileInfo->getExtension() === 'yml') {
$this->processYamlFile($fileInfo);
}
} catch (AnalysedCodeException $analysedCodeException) {
if ($shouldHideAutoloadErrors) {
continue;
}
$message = sprintf(
'Analyze error: %s Try to include your files in "parameters > autoload_files" or "parameters > autoload_directories".%sSee https://github.com/rectorphp/rector#extra-autoloading',
$analysedCodeException->getMessage(),
PHP_EOL
);
$this->errors[] = new Error($fileInfo, $message, null);
} catch (Throwable $throwable) {
$this->errors[] = new Error($fileInfo, $throwable->getMessage(), $throwable->getCode());
}
$this->processFileInfo($fileInfo, $shouldHideAutoloadErrors);
$this->consoleStyle->progressAdvance();
}
@ -271,4 +248,31 @@ final class ProcessCommand extends Command
}
}
}
private function processFileInfo(SplFileInfo $fileInfo, bool $shouldHideAutoloadErrors): void
{
try {
// php
if ($fileInfo->getExtension() === 'php') {
$this->processFile($fileInfo);
// yml
} elseif ($fileInfo->getExtension() === 'yml') {
$this->processYamlFile($fileInfo);
}
} catch (AnalysedCodeException $analysedCodeException) {
if ($shouldHideAutoloadErrors) {
return;
}
$message = sprintf(
'Analyze error: %s Try to include your files in "parameters > autoload_files" or "parameters > autoload_directories".%sSee https://github.com/rectorphp/rector#extra-autoloading',
$analysedCodeException->getMessage(),
PHP_EOL
);
$this->errors[] = new Error($fileInfo, $message, null);
} catch (Throwable $throwable) {
$this->errors[] = new Error($fileInfo, $throwable->getMessage(), $throwable->getCode());
}
}
}

View File

@ -107,7 +107,7 @@ final class FilesFinder
$finder->filter(function (NativeSplFileInfo $splFileInfo) {
// return false to remove file
foreach ($this->excludePaths as $excludePath) {
if (Strings::match($splFileInfo->getRealPath(), '#' . preg_quote($excludePath, '#') . '#' )) {
if (Strings::match($splFileInfo->getRealPath(), '#' . preg_quote($excludePath, '#') . '#')) {
return false;
}