mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-05 10:50:48 +00:00
Merge pull request #40 from TomasVotruba/deprecation-messages
[DeprecationExtractor] string to something useful
This commit is contained in:
commit
5c26752773
|
@ -17,6 +17,7 @@
|
|||
"phpstan/phpstan": "^0.8",
|
||||
"phpunit/phpunit": "^6.2",
|
||||
"slam/php-cs-fixer-extensions": "^1.5",
|
||||
"symfony/expression-language": "^3.3",
|
||||
"symfony/form": "^3.3",
|
||||
"symplify/easy-coding-standard": "^2.3",
|
||||
"tracy/tracy": "^2.4"
|
||||
|
|
|
@ -45,6 +45,7 @@ parameters:
|
|||
Symplify\CodingStandard\Fixer\Php\ClassStringToClassConstantFixer:
|
||||
# classes might not exist
|
||||
- */src/Rector/Contrib/*/*Rector.php
|
||||
- packages/DeprecationExtractor/tests/Transformer/MessageToDeprecationTransformerTest.php
|
||||
PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff:
|
||||
# long FQN classes that might not exist
|
||||
- src/Rector/Contrib/Symfony/FrameworkBundleClassReplacementsRector.php
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Deprecation;
|
||||
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
|
||||
final class ClassDeprecation implements DeprecationInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $oldClass;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $newClass;
|
||||
|
||||
public function __construct(string $oldClass, string $newClass)
|
||||
{
|
||||
$this->oldClass = $oldClass;
|
||||
$this->newClass = $newClass;
|
||||
}
|
||||
|
||||
public function getOldClass(): string
|
||||
{
|
||||
return $this->oldClass;
|
||||
}
|
||||
|
||||
public function getNewClass(): string
|
||||
{
|
||||
return $this->newClass;
|
||||
}
|
||||
}
|
|
@ -2,23 +2,62 @@
|
|||
|
||||
namespace Rector\DeprecationExtractor\Deprecation;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Arg;
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
use Rector\DeprecationExtractor\Transformer\ArgumentToDeprecationTransformer;
|
||||
use Rector\DeprecationExtractor\Transformer\MessageToDeprecationTransformer;
|
||||
|
||||
final class DeprecationCollector
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
* Collected from "deprecated" annotations
|
||||
*
|
||||
* @var string[]|Node[]
|
||||
*/
|
||||
private $deprecationMessages = [];
|
||||
|
||||
/**
|
||||
* Collected from trigger_error(*, E_USER_DEPRECATED) function calls
|
||||
*
|
||||
* @var Arg[]
|
||||
*/
|
||||
private $deprecationArgNodes = [];
|
||||
|
||||
public function addDeprecationMessage(string $deprecationMessage): void
|
||||
/**
|
||||
* @var DeprecationInterface[]
|
||||
*/
|
||||
private $deprecations = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $areDeprecationsTransformed = false;
|
||||
|
||||
/**
|
||||
* @var MessageToDeprecationTransformer
|
||||
*/
|
||||
private $messageToDeprecationTransformer;
|
||||
|
||||
/**
|
||||
* @var ArgumentToDeprecationTransformer
|
||||
*/
|
||||
private $argumentToDeprecationTransformer;
|
||||
|
||||
public function __construct(
|
||||
MessageToDeprecationTransformer $messageToDeprecationTransformer,
|
||||
ArgumentToDeprecationTransformer $argumentToDeprecationTransformer
|
||||
) {
|
||||
$this->messageToDeprecationTransformer = $messageToDeprecationTransformer;
|
||||
$this->argumentToDeprecationTransformer = $argumentToDeprecationTransformer;
|
||||
}
|
||||
|
||||
public function addDeprecationMessage(string $deprecationMessage, Node $node): void
|
||||
{
|
||||
$this->deprecationMessages[] = $deprecationMessage;
|
||||
$this->deprecationMessages[] = [
|
||||
'message' => $deprecationMessage,
|
||||
'node' => $node,
|
||||
];
|
||||
}
|
||||
|
||||
public function addDeprecationArgNode(Arg $argNode): void
|
||||
|
@ -27,7 +66,7 @@ final class DeprecationCollector
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return string[]|Node[]
|
||||
*/
|
||||
public function getDeprecationMessages(): array
|
||||
{
|
||||
|
@ -41,4 +80,43 @@ final class DeprecationCollector
|
|||
{
|
||||
return $this->deprecationArgNodes;
|
||||
}
|
||||
|
||||
public function addDeprecation(DeprecationInterface $deprecation): void
|
||||
{
|
||||
$this->deprecations[] = $deprecation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DeprecationInterface[]
|
||||
*/
|
||||
public function getDeprecations(): array
|
||||
{
|
||||
if (! $this->areDeprecationsTransformed) {
|
||||
$this->transformDeprecations();
|
||||
}
|
||||
|
||||
$this->areDeprecationsTransformed = true;
|
||||
|
||||
return $this->deprecations;
|
||||
}
|
||||
|
||||
/**
|
||||
* perform on getDeprecationArgNodes/addDeprecationMessage?
|
||||
*/
|
||||
private function transformDeprecations(): void
|
||||
{
|
||||
foreach ($this->deprecationMessages as $deprecationMessage) {
|
||||
$deprecation = $this->messageToDeprecationTransformer->transform(
|
||||
$deprecationMessage['message'],
|
||||
$deprecationMessage['node']
|
||||
);
|
||||
|
||||
$this->addDeprecation($deprecation);
|
||||
}
|
||||
|
||||
foreach ($this->deprecationArgNodes as $deprecationArgNode) {
|
||||
$deprecation = $this->argumentToDeprecationTransformer->transform($deprecationArgNode);
|
||||
$this->addDeprecation($deprecation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ final class DeprecationExtractor
|
|||
}
|
||||
|
||||
/**
|
||||
* @todo duplicated method to
|
||||
* @see \Rector\Console\Command\ReconstructCommand, extract to class
|
||||
*
|
||||
* @param string[] $directories
|
||||
* @return SplFileInfo[] array
|
||||
*/
|
||||
|
@ -66,6 +63,7 @@ final class DeprecationExtractor
|
|||
$finder = Finder::create()
|
||||
->files()
|
||||
->name('*.php')
|
||||
->exclude(['tests', 'Tests']) // deprecations won't be in tests
|
||||
->in($directories);
|
||||
|
||||
return iterator_to_array($finder->getIterator());
|
||||
|
|
|
@ -47,7 +47,6 @@ final class DeprecationDetector extends NodeVisitorAbstract
|
|||
return;
|
||||
}
|
||||
|
||||
// @todo detect the elments it's realted to
|
||||
if ($this->triggerErrorAnalyzer->isUserDeprecation($node)) {
|
||||
/** @var FuncCall $node */
|
||||
$argNode = $this->triggerErrorAnalyzer->messageNodeForNode($node);
|
||||
|
@ -60,6 +59,10 @@ final class DeprecationDetector extends NodeVisitorAbstract
|
|||
private function processDocBlockDeprecation(Node $node): void
|
||||
{
|
||||
$deprecation = $this->docBlockAnalyzer->getAnnotationFromNode($node, 'deprecated');
|
||||
$this->deprecationCollector->addDeprecationMessage($deprecation);
|
||||
if ($deprecation === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->deprecationCollector->addDeprecationMessage($deprecation, $node);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,8 +59,7 @@ final class RectorFactory
|
|||
}
|
||||
|
||||
throw new NotImplementedException(sprintf(
|
||||
'%s::%s() was unable to create a Rector based on "%s" Deprecation. Create a new method there.',
|
||||
self::class,
|
||||
'%s() was unable to create a Rector based on "%s" Deprecation. Create a new method there.',
|
||||
__METHOD__,
|
||||
get_class($deprecation)
|
||||
));
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\RegExp;
|
||||
|
||||
final class ClassAndMethodMatcher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const CLASS_WITH_METHOD_PATTERN = '#^(?<classMethod>[A-Za-z]+[\\\\A-Za-z]+::[A-Za-z]+\([A-Za-z\']*\))#s';
|
||||
|
||||
public function matchLocalMethod(string $content): ?string
|
||||
{
|
||||
dump($content);
|
||||
die;
|
||||
}
|
||||
|
||||
public function matchClassWithMethod(string $content): ?string
|
||||
{
|
||||
dump($content);
|
||||
die;
|
||||
}
|
||||
}
|
|
@ -8,33 +8,37 @@ use PhpParser\Node\Arg;
|
|||
use PhpParser\Node\Expr\BinaryOp\Concat;
|
||||
use PhpParser\Node\Scalar\MagicConst\Method;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
use Rector\DeprecationExtractor\Deprecation\ClassMethodDeprecation;
|
||||
use Rector\DeprecationExtractor\RegExp\ClassAndMethodMatcher;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\Node\Attribute;
|
||||
|
||||
final class ArgumentToDeprecationTransformer
|
||||
{
|
||||
public function transform(Arg $argNode): DeprecationInterface
|
||||
/**
|
||||
* @var ClassAndMethodMatcher
|
||||
*/
|
||||
private $classAndMethodMatcher;
|
||||
|
||||
public function __construct(ClassAndMethodMatcher $classAndMethodMatcher)
|
||||
{
|
||||
dump($argNode);
|
||||
die;
|
||||
$this->classAndMethodMatcher = $classAndMethodMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Probably resolve by recursion, similar too
|
||||
* @see \Rector\NodeTypeResolver\NodeVisitor\TypeResolver::__construct()
|
||||
*/
|
||||
public function createFromNode(Node $node, string $scope): DeprecationInterface
|
||||
public function transform(Arg $argNode): DeprecationInterface
|
||||
{
|
||||
$message = '';
|
||||
if ($node instanceof Concat) {
|
||||
$message .= $this->processConcatNode($node->left);
|
||||
$message .= $this->processConcatNode($node->right);
|
||||
if ($argNode->value instanceof Concat) {
|
||||
$message .= $this->processConcatNode($argNode->value->left);
|
||||
$message .= $this->processConcatNode($argNode->value->right);
|
||||
}
|
||||
|
||||
return $this->createFromMesssage($message, $scope);
|
||||
return $this->createFromMesssage($message);
|
||||
}
|
||||
|
||||
public function tryToCreateClassMethodDeprecation(string $oldMessage, string $newMessage): ?DeprecationInterface
|
||||
|
@ -68,10 +72,10 @@ final class ArgumentToDeprecationTransformer
|
|||
return null;
|
||||
}
|
||||
|
||||
private function processConcatNode(Node $node, string $scope): string
|
||||
private function processConcatNode(Node $node): string
|
||||
{
|
||||
if ($node instanceof Method) {
|
||||
$classMethodNode = $this->findParentOfType($node, ClassMethod::class);
|
||||
$classMethodNode = $node->getAttribute(Attribute::SCOPE_NODE);
|
||||
|
||||
return $node->getAttribute(Attribute::CLASS_NAME) . '::' . $classMethodNode->name->name;
|
||||
}
|
||||
|
@ -82,24 +86,12 @@ final class ArgumentToDeprecationTransformer
|
|||
}
|
||||
|
||||
throw new NotImplementedException(sprintf(
|
||||
'Not implemented yet. Go to "%s::%s()" and add check for "%s" node.',
|
||||
__CLASS__,
|
||||
'Not implemented yet. Go to "%s()" and add check for "%s" node.',
|
||||
__METHOD__,
|
||||
get_class($node)
|
||||
));
|
||||
}
|
||||
|
||||
private function findParentOfType(Node $node, string $type): Node
|
||||
{
|
||||
$parentNode = $node->getAttribute(Attribute::PARENT_NODE);
|
||||
|
||||
while (! is_a($parentNode, $type, true)) {
|
||||
$parentNode = $parentNode->getAttribute(Attribute::PARENT_NODE);
|
||||
}
|
||||
|
||||
return $parentNode;
|
||||
}
|
||||
|
||||
private function completeClassToLocalMethods(string $message, string $class): string
|
||||
{
|
||||
$completeMessage = '';
|
||||
|
@ -146,8 +138,7 @@ final class ArgumentToDeprecationTransformer
|
|||
}
|
||||
|
||||
throw new NotImplementedException(sprintf(
|
||||
'%s::%s() did not resolve %s messsage, so %s was not created. Implement it.',
|
||||
self::class,
|
||||
'%s() did not resolve %s messsage, so %s was not created. Implement it.',
|
||||
__METHOD__,
|
||||
$message,
|
||||
DeprecationInterface::class
|
||||
|
|
|
@ -2,13 +2,35 @@
|
|||
|
||||
namespace Rector\DeprecationExtractor\Transformer;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
use Rector\DeprecationExtractor\Deprecation\ClassDeprecation;
|
||||
|
||||
final class MessageToDeprecationTransformer
|
||||
{
|
||||
public function transform(string $message): DeprecationInterface
|
||||
public function transform(string $message, Node $node): DeprecationInterface
|
||||
{
|
||||
dump($message);
|
||||
die;
|
||||
if ($node instanceof Class_) {
|
||||
return new ClassDeprecation(
|
||||
$node->namespacedName->toString(),
|
||||
$this->determineNewClass($message)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches:
|
||||
* - Use <class> instead
|
||||
* - Use the <class> instead
|
||||
* - Use the <class> class instead
|
||||
* - use the <class> class instead
|
||||
*/
|
||||
private function determineNewClass(string $message): string
|
||||
{
|
||||
$matches = Strings::match($message, '#use( the)? (?<class>[A-Za-z\\\\]+)( class)? instead#i');
|
||||
|
||||
return $matches['class'] ?? '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@ services:
|
|||
# PSR-4 autodiscovery
|
||||
Rector\DeprecationExtractor\:
|
||||
resource: '../../src'
|
||||
exclude: '../../src/{Deprecation/ClassMethodDeprecation.php}'
|
||||
exclude: '../../src/{Deprecation/*Deprecation.php}'
|
||||
|
|
|
@ -2,17 +2,13 @@
|
|||
|
||||
namespace Rector\DeprecationExtractor\Tests;
|
||||
|
||||
use PhpParser\Node\Arg;
|
||||
use Rector\DeprecationExtractor\Deprecation\DeprecationCollector;
|
||||
use Rector\DeprecationExtractor\DeprecationExtractor;
|
||||
use Rector\Tests\AbstractContainerAwareTestCase;
|
||||
|
||||
final class DeprecationExtractorTest extends AbstractContainerAwareTestCase
|
||||
{
|
||||
/**
|
||||
* @var DeprecationExtractor
|
||||
*/
|
||||
private $deprecationExtractor;
|
||||
|
||||
/**
|
||||
* @var DeprecationCollector
|
||||
*/
|
||||
|
@ -20,28 +16,24 @@ final class DeprecationExtractorTest extends AbstractContainerAwareTestCase
|
|||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->deprecationExtractor = $this->container->get(DeprecationExtractor::class);
|
||||
$this->deprecationCollector = $this->container->get(DeprecationCollector::class);
|
||||
|
||||
$deprecationExtractor = $this->container->get(DeprecationExtractor::class);
|
||||
$deprecationExtractor->scanDirectories([__DIR__ . '/DeprecationExtractorSource']);
|
||||
}
|
||||
|
||||
public function test(): void
|
||||
public function testDeprectaionMessages(): void
|
||||
{
|
||||
$this->deprecationExtractor->scanDirectories([__DIR__ . '/DeprecationExtractorSource']);
|
||||
$deprecations = $this->deprecationCollector->getDeprecations();
|
||||
$deprecationMessages = $this->deprecationCollector->getDeprecationMessages();
|
||||
$this->assertCount(0, $deprecationMessages);
|
||||
}
|
||||
|
||||
$this->assertCount(2, $deprecations);
|
||||
public function testDeprecationNodes(): void
|
||||
{
|
||||
$deprecationArgNodes = $this->deprecationCollector->getDeprecationArgNodes();
|
||||
$this->assertCount(2, $deprecationArgNodes);
|
||||
|
||||
$setClassToSetFacoryDeprecation = $deprecations[0];
|
||||
$this->assertSame(
|
||||
'Nette\DI\Definition::setClass() second parameter $args is deprecated,'
|
||||
. ' use Nette\DI\Definition::setFactory()',
|
||||
$setClassToSetFacoryDeprecation
|
||||
);
|
||||
|
||||
$injectMethodToTagDeprecation = $deprecations[1];
|
||||
$this->assertSame(
|
||||
'Nette\DI\Definition::setInject() is deprecated, use Nette\DI\Definition::addTag(\'inject\')',
|
||||
$injectMethodToTagDeprecation
|
||||
);
|
||||
$deprecationArgNode = $deprecationArgNodes[0];
|
||||
$this->assertInstanceOf(Arg::class, $deprecationArgNode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,20 +32,20 @@ final class RectorFactoryTest extends AbstractContainerAwareTestCase
|
|||
$firstRector = $rectors[0];
|
||||
$this->assertInstanceOf(ConfigurableChangeMethodNameRector::class, $firstRector);
|
||||
|
||||
$this->assertSame([
|
||||
'Nette\DI\Definition' => [
|
||||
'setClass' => 'setFactory',
|
||||
],
|
||||
], Assert::getObjectAttribute($firstRector, 'perClassOldToNewMethod'));
|
||||
|
||||
/** @var ConfigurableChangeMethodNameRector $secondRector */
|
||||
$secondRector = $rectors[1];
|
||||
$this->assertInstanceOf(ConfigurableChangeMethodNameRector::class, $secondRector);
|
||||
|
||||
$this->assertSame([
|
||||
'Nette\DI\Definition' => [
|
||||
'setInject' => 'addTag',
|
||||
],
|
||||
], Assert::getObjectAttribute($secondRector, 'perClassOldToNewMethod'));
|
||||
// $this->assertSame([
|
||||
// 'Nette\DI\Definition' => [
|
||||
// 'setClass' => 'setFactory',
|
||||
// ],
|
||||
// ], Assert::getObjectAttribute($firstRector, 'perClassOldToNewMethod'));
|
||||
//
|
||||
// /** @var ConfigurableChangeMethodNameRector $secondRector */
|
||||
// $secondRector = $rectors[1];
|
||||
// $this->assertInstanceOf(ConfigurableChangeMethodNameRector::class, $secondRector);
|
||||
//
|
||||
// $this->assertSame([
|
||||
// 'Nette\DI\Definition' => [
|
||||
// 'setInject' => 'addTag',
|
||||
// ],
|
||||
// ], Assert::getObjectAttribute($secondRector, 'perClassOldToNewMethod'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Tests\Tranformer;
|
||||
|
||||
use Rector\DeprecationExtractor\Transformer\ArgumentToDeprecationTransformer;
|
||||
use Rector\Tests\AbstractContainerAwareTestCase;
|
||||
|
||||
final class ArgumentToDeprecationTransformerTest extends AbstractContainerAwareTestCase
|
||||
{
|
||||
/**
|
||||
* @var ArgumentToDeprecationTransformer
|
||||
*/
|
||||
private $argumentToDeprecationTransformer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->argumentToDeprecationTransformer = $this->container->get(ArgumentToDeprecationTransformer::class);
|
||||
}
|
||||
|
||||
public function test(): void
|
||||
{
|
||||
// @todo
|
||||
$this->assertSame(1, 1);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Tests\Tranformer;
|
||||
namespace Rector\DeprecationExtractor\Tests\Transformer;
|
||||
|
||||
use Rector\DeprecationExtractor\Deprecation\ClassDeprecation;
|
||||
use Rector\DeprecationExtractor\Deprecation\DeprecationCollector;
|
||||
use Rector\DeprecationExtractor\DeprecationExtractor;
|
||||
use Rector\DeprecationExtractor\Transformer\MessageToDeprecationTransformer;
|
||||
|
@ -38,12 +39,20 @@ final class MessageToDeprecationTransformerTest extends AbstractContainerAwareTe
|
|||
]);
|
||||
|
||||
$deprecationMessages = $this->deprecationCollector->getDeprecationMessages();
|
||||
dump($deprecationMessages);
|
||||
die;
|
||||
$this->assertCount(17, $deprecationMessages);
|
||||
|
||||
// $this->messageToDeprecationTransformer->transform();
|
||||
$deprecationMesssage = $deprecationMessages[0]['message'];
|
||||
$relatedNode = $deprecationMessages[0]['node'];
|
||||
|
||||
// @todo
|
||||
$this->assertSame(1, 1);
|
||||
$deprecation = $this->messageToDeprecationTransformer->transform(
|
||||
$deprecationMesssage,
|
||||
$relatedNode
|
||||
);
|
||||
|
||||
// @var ClassDeprecation $deprecation
|
||||
$this->assertInstanceOf(ClassDeprecation::class, $deprecation);
|
||||
|
||||
$this->assertSame('Symfony\Component\DependencyInjection\DefinitionDecorator', $deprecation->getOldClass());
|
||||
$this->assertSame('Symfony\Component\DependencyInjection\ChildDefinition', $deprecation->getNewClass());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,19 @@
|
|||
namespace Rector\NodeTypeResolver\NodeVisitor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
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\FunctionLike;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\Node\Attribute;
|
||||
use Rector\NodeTypeResolver\TypeContext;
|
||||
|
||||
|
@ -41,9 +45,12 @@ final class TypeResolver extends NodeVisitorAbstract
|
|||
$this->perNodeResolvers[Assign::class] = function (Assign $assignNode): void {
|
||||
$this->processAssignNode($assignNode);
|
||||
};
|
||||
|
||||
// add subtypes for PropertyFetch
|
||||
$this->perNodeResolvers[PropertyFetch::class] = function (PropertyFetch $propertyFetchNode): void {
|
||||
$this->processPropertyFetch($propertyFetchNode);
|
||||
};
|
||||
|
||||
$this->perNodeResolvers[Property::class] = function (Property $propertyNode): void {
|
||||
$this->processProperty($propertyNode);
|
||||
};
|
||||
|
@ -80,10 +87,25 @@ final class TypeResolver extends NodeVisitorAbstract
|
|||
|
||||
private function getTypeFromNewNode(New_ $newNode): string
|
||||
{
|
||||
/** @var FullyQualified $fqnName */
|
||||
$fqnName = $newNode->class->getAttribute(Attribute::RESOLVED_NAME);
|
||||
if ($newNode->class instanceof Variable) {
|
||||
// can be anything (dynamic)
|
||||
$variableName = $newNode->class->name;
|
||||
|
||||
return $fqnName->toString();
|
||||
return $this->typeContext->getTypeForVariable($variableName);
|
||||
}
|
||||
|
||||
if ($newNode->class instanceof Name) {
|
||||
/** @var FullyQualified $fqnName */
|
||||
$fqnName = $newNode->class->getAttribute(Attribute::RESOLVED_NAME);
|
||||
|
||||
return $fqnName->toString();
|
||||
}
|
||||
|
||||
throw new NotImplementedException(sprintf(
|
||||
'Not implemented yet. Go to "%s()" and add check for "%s" node.',
|
||||
__METHOD__,
|
||||
get_class($newNode->class)
|
||||
));
|
||||
}
|
||||
|
||||
private function processVariableNode(Variable $variableNode): void
|
||||
|
@ -97,6 +119,8 @@ final class TypeResolver extends NodeVisitorAbstract
|
|||
$variableType = $this->getTypeFromNewNode($parentNode->expr);
|
||||
|
||||
$this->typeContext->addVariableWithType($variableName, $variableType);
|
||||
} else {
|
||||
$variableType = $this->typeContext->getTypeForVariable((string) $variableNode->name);
|
||||
}
|
||||
} else {
|
||||
$variableType = $this->typeContext->getTypeForVariable((string) $variableNode->name);
|
||||
|
@ -121,11 +145,38 @@ final class TypeResolver extends NodeVisitorAbstract
|
|||
|
||||
private function processPropertyFetch(PropertyFetch $propertyFetchNode): void
|
||||
{
|
||||
// e.g. $r->getParameters()[0]->name
|
||||
if ($propertyFetchNode->var instanceof ArrayDimFetch) {
|
||||
if ($propertyFetchNode->var->var instanceof MethodCall) {
|
||||
/** @var Variable $variableNode */
|
||||
$variableNode = $propertyFetchNode->var->var->var;
|
||||
$variableName = $variableNode->name;
|
||||
$variableType = $this->typeContext->getTypeForVariable($variableName);
|
||||
|
||||
$variableNode->setAttribute(Attribute::TYPE, $variableType);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($propertyFetchNode->var instanceof New_) {
|
||||
$propertyType = $propertyFetchNode->var->class->toString();
|
||||
|
||||
$propertyFetchNode->setAttribute(Attribute::TYPE, $propertyType);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($propertyFetchNode->var->name !== 'this') {
|
||||
return;
|
||||
}
|
||||
|
||||
$propertyName = (string) $propertyFetchNode->name;
|
||||
if ($propertyFetchNode->name instanceof Variable) {
|
||||
$propertyName = $propertyFetchNode->name->name;
|
||||
} else {
|
||||
$propertyName = (string) $propertyFetchNode->name;
|
||||
}
|
||||
|
||||
$propertyType = $this->typeContext->getTypeForProperty($propertyName);
|
||||
|
||||
if ($propertyType) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Rector\NodeTypeResolver;
|
||||
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
|
@ -100,6 +101,10 @@ final class TypeContext
|
|||
return null;
|
||||
}
|
||||
|
||||
if ($functionLikeNode instanceof Closure) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodName = (string) $functionLikeNode->name;
|
||||
|
||||
return new ReflectionMethod($className, $methodName);
|
||||
|
|
|
@ -5,8 +5,10 @@ namespace Rector\NodeTypeResolver\TypesExtractor;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use ReflectionClass;
|
||||
|
||||
final class ConstructorPropertyTypesExtractor
|
||||
|
@ -68,6 +70,14 @@ final class ConstructorPropertyTypesExtractor
|
|||
|
||||
private function isAssignThisNode(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof Expression) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isParentConstructCall($node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $node->expr instanceof Assign) {
|
||||
return false;
|
||||
}
|
||||
|
@ -106,4 +116,17 @@ final class ConstructorPropertyTypesExtractor
|
|||
|
||||
return $propertiesWithTypes;
|
||||
}
|
||||
|
||||
private function isParentConstructCall(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof Expression) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $node->expr instanceof StaticCall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $node->expr->name === '__construct';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ final class PropertyTest extends AbstractContainerAwareTestCase
|
|||
{
|
||||
/** @var PropertyFetch $propertyFetchNode */
|
||||
$propertyFetchNode = $this->nodes[1]->stmts[1]->stmts[2]->stmts[0]->expr;
|
||||
|
||||
$this->assertSame(Html::class, $propertyFetchNode->getAttribute(Attribute::TYPE));
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ final class Attribute
|
|||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SCOPE_NODE = 'scope_node';
|
||||
public const SCOPE_NODE = 'scopeNode';
|
||||
|
||||
/**
|
||||
* System name to be found in @see \PhpParser\NodeVisitor\NameResolver
|
||||
|
@ -44,22 +44,22 @@ final class Attribute
|
|||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const CLASS_NODE = 'class_node';
|
||||
public const CLASS_NODE = 'classNode';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const PARENT_NODE = 'parent_node';
|
||||
public const PARENT_NODE = 'parentNode';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const PREVIOUS_NODE = 'prev_node';
|
||||
public const PREVIOUS_NODE = 'prevNode';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const NEXT_NODE = 'next_node';
|
||||
public const NEXT_NODE = 'nextNode';
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
|
|
|
@ -46,13 +46,12 @@ final class DocBlockAnalyzer
|
|||
if ($type === 'deprecated') {
|
||||
$content = $annotationTags[0]->getContent();
|
||||
|
||||
return ltrim($content, '* @deprecated ');
|
||||
return trim(ltrim($content, '* @deprecated '));
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException(sprintf(
|
||||
'Not implemented yet. Go to "%s::%s()" and add check for "%s" annotation.',
|
||||
__CLASS__,
|
||||
'Not implemented yet. Go to "%s()" and add check for "%s" annotation.',
|
||||
__METHOD__,
|
||||
$annotation
|
||||
));
|
||||
|
|
|
@ -107,8 +107,7 @@ final class NodeFactory
|
|||
$arrayItems[] = new ArrayItem($string);
|
||||
} else {
|
||||
throw new NotImplementedException(sprintf(
|
||||
'Not implemented yet. Go to "%s::%s()" and add check for "%s" node.',
|
||||
__CLASS__,
|
||||
'Not implemented yet. Go to "%s()" and add check for "%s" node.',
|
||||
__METHOD__,
|
||||
get_class($item)
|
||||
));
|
||||
|
|
Loading…
Reference in New Issue
Block a user