Merge pull request #787 from rectorphp/phpstorm-doc-fixer

[PHPStan] Add Phpstorm doc fixer
This commit is contained in:
Tomáš Votruba 2018-11-17 11:16:48 +02:00 committed by GitHub
commit 908044bff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 226 additions and 1 deletions

View File

@ -1,2 +1,3 @@
services:
Rector\PHPStan\Rector\Cast\RecastingRemovalRector: ~
Rector\PHPStan\Rector\Node\PHPStormVarAnnotationRector: ~

View File

@ -4,6 +4,7 @@ namespace Rector\ContributorTools\Configuration;
use Nette\Loaders\RobotLoader;
use Nette\Utils\Strings;
use PhpParser\Node;
use Rector\ContributorTools\Exception\ConfigurationException;
use Rector\Exception\FileSystem\FileNotFoundException;
use Symfony\Component\Finder\Finder;
@ -99,6 +100,11 @@ final class ConfigurationFactory
$fqnNodeTypes = [];
foreach ($nodeTypes as $nodeType) {
if ($nodeType === 'Node') {
$fqnNodeTypes[] = Node::class;
continue;
}
foreach ($nodeClasses as $nodeClass) {
if ($this->isNodeClassMatch($nodeClass, $nodeType)) {
$fqnNodeTypes[] = $nodeClass;

View File

@ -3,8 +3,8 @@
namespace Rector\Jms\Rector\Property;
use Nette\Utils\Strings;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use Rector\Application\ErrorCollector;

View File

@ -0,0 +1,140 @@
<?php declare(strict_types=1);
namespace Rector\PHPStan\Rector\Assign;
use Nette\Utils\Strings;
use PhpParser\Comment\Doc;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Nop;
use Rector\NodeTypeResolver\Node\Attribute;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see https://github.com/shopsys/shopsys/pull/524
*/
final class PHPStormVarAnnotationRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Change various @var annotation formats to one PHPStorm understands', [
new CodeSample(
<<<'CODE_SAMPLE'
$config = 5;
/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */
$config = 5;
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Assign::class];
}
/**
* @param Assign $node
*/
public function refactor(Node $node): ?Node
{
/** @var Expression $expression */
$expression = $node->getAttribute(Attribute::CURRENT_EXPRESSION);
/** @var Node|null $nextNode */
$nextNode = $expression->getAttribute(Attribute::NEXT_NODE);
if ($nextNode === null) {
return null;
}
$docContent = $this->getDocContent($nextNode);
if ($docContent === '') {
return null;
}
if (! Strings::contains($docContent, '@var')) {
return null;
}
if (! $node->var instanceof Variable) {
return null;
}
$varName = '$' . $this->getName($node->var);
$varPattern = '# ' . preg_quote($varName, '#') . ' #';
if (! Strings::match($docContent, $varPattern)) {
return null;
}
// switch docs
$expression->setDocComment($this->createDocComment($nextNode));
// invoke override
$expression->setAttribute(Attribute::ORIGINAL_NODE, null);
// remove otherwise empty node
if ($nextNode instanceof Nop) {
$this->removeNode($nextNode);
return null;
}
$nextNode->setAttribute('comments', null);
return $node;
}
private function getDocContent(Node $node): string
{
if ($node->getDocComment()) {
return $node->getDocComment()->getText();
}
if ($node->getComments()) {
$docContent = '';
foreach ($node->getComments() as $comment) {
$docContent .= $comment->getText();
}
return $docContent;
}
return '';
}
private function createDocComment(Node $node): Doc
{
if ($node->getDocComment() !== null) {
return $node->getDocComment();
}
$docContent = $this->getDocContent($node);
// normalize content
// starts with "/*", instead of "/**"
if (Strings::startsWith($docContent, '/* ')) {
$docContent = Strings::replace($docContent, '#^\/\* #', '/** ');
}
// $value is first, instead of type is first
if (Strings::match($docContent, '#\@var(\s)+\$#')) {
$docContent = Strings::replace(
$docContent,
'#(?<variableName>\$\w+)(?<space>\s+)(?<type>[\\\\\w]+)#',
'$3$2$1'
);
}
return new Doc($docContent);
}
}

View File

@ -0,0 +1,4 @@
<?php
/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */
$config = 5;

View File

@ -0,0 +1,5 @@
<?php
/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */
$config = 5;
$unrelatedValue = 10;

View File

@ -0,0 +1,6 @@
<?php
/** @var \SomeType $value */
$value = $this->getSomeType();
/** @var \SomeType $value2 */
$value2 = $this->getSomeType();

View File

@ -0,0 +1,6 @@
<?php
/** @var \Doctrine\ORM\EntityManager $em */
$em = $this->getContainer()->get('doctrine.orm.entity_manager');
/** @var \Doctrine\ORM\QueryBuilder $queryBuilder */
$queryBuilder = $em->createQueryBuilder();

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace Rector\PHPStan\Tests\Rector\Assign\PHPStormVarAnnotationRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
/**
* @covers \Rector\PHPStan\Rector\Assign\PHPStormVarAnnotationRector
*/
final class PHPStormVarAnnotationRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideWrongToFixedFiles()
*/
public function test(string $wrong, string $fixed): void
{
$this->doTestFileMatchesExpectedContent($wrong, $fixed);
}
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/wrong4.php.inc', __DIR__ . '/Correct/correct4.php.inc'];
}
protected function provideConfig(): string
{
return __DIR__ . '/config.yml';
}
}

View File

@ -0,0 +1,4 @@
<?php
$config = 5;
/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */

View File

@ -0,0 +1,5 @@
<?php
$config = 5;
/** @var \Shopsys\FrameworkBundle\Model\Product\Filter\ProductFilterConfig $config */
$unrelatedValue = 10;

View File

@ -0,0 +1,7 @@
<?php
$value = $this->getSomeType();
/* @var $value \SomeType */
$value2 = $this->getSomeType();
/* @var \SomeType $value2 */

View File

@ -0,0 +1,6 @@
<?php
$em = $this->getContainer()->get('doctrine.orm.entity_manager');
/* @var $em \Doctrine\ORM\EntityManager */
$queryBuilder = $em->createQueryBuilder();
/* @var $queryBuilder \Doctrine\ORM\QueryBuilder */

View File

@ -0,0 +1,2 @@
services:
Rector\PHPStan\Rector\Assign\PHPStormVarAnnotationRector: ~