mirror of
https://github.com/rectorphp/rector.git
synced 2025-03-14 04:19:44 +01:00
improve chain method call resolutuin
This commit is contained in:
parent
0c3359e217
commit
eb88378488
@ -1,4 +1,4 @@
|
||||
# All 465 Rectors Overview
|
||||
# All 466 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
- [General](#general)
|
||||
@ -5333,6 +5333,38 @@ Turns true/false comparisons to their method name alternatives in PHPUnit TestCa
|
||||
|
||||
<br>
|
||||
|
||||
### `CreateMockToCreateStubRector`
|
||||
|
||||
- class: [`Rector\PHPUnit\Rector\MethodCall\CreateMockToCreateStubRector`](/../master/rules/phpunit/src/Rector/MethodCall/CreateMockToCreateStubRector.php)
|
||||
- [test fixtures](/../master/rules/phpunit/tests/Rector/MethodCall/CreateMockToCreateStubRector/Fixture)
|
||||
|
||||
Replaces createMock() with createStub() when relevant
|
||||
|
||||
```diff
|
||||
use PHPUnit\Framework\TestCase
|
||||
|
||||
class MyTest extends TestCase
|
||||
{
|
||||
public function testItBehavesAsExpected(): void
|
||||
{
|
||||
- $stub = $this->createMock(\Exception::class);
|
||||
+ $stub = $this->createStub(\Exception::class);
|
||||
$stub->method('getMessage')
|
||||
->willReturn('a message');
|
||||
|
||||
$mock = $this->createMock(\Exception::class);
|
||||
$mock->expects($this->once())
|
||||
->method('getMessage')
|
||||
->willReturn('a message');
|
||||
|
||||
self::assertSame('a message', $stub->getMessage());
|
||||
self::assertSame('a message', $mock->getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### `DelegateExceptionArgumentsRector`
|
||||
|
||||
- class: [`Rector\PHPUnit\Rector\DelegateExceptionArgumentsRector`](/../master/rules/phpunit/src/Rector/DelegateExceptionArgumentsRector.php)
|
||||
|
@ -140,5 +140,5 @@ final class AttributeKey
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const METHOD_CALL_NODE_VARIABLE = 'method_call_variable';
|
||||
public const METHOD_CALL_NODE_CALLER_NAME = 'method_call_variable_name';
|
||||
}
|
||||
|
@ -5,16 +5,30 @@ declare(strict_types=1);
|
||||
namespace Rector\NodeTypeResolver\NodeVisitor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class MethodCallNodeVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var Node\Expr
|
||||
* @var Expr|Name
|
||||
*/
|
||||
private $currentCaller;
|
||||
private $callerNode;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver)
|
||||
{
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|Node|void|null
|
||||
@ -32,12 +46,22 @@ final class MethodCallNodeVisitor extends NodeVisitorAbstract
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $node->var instanceof MethodCall) {
|
||||
$this->currentCaller = $node->var;
|
||||
$callerNode = $node->var;
|
||||
if ($callerNode instanceof MethodCall) {
|
||||
while ($callerNode instanceof MethodCall) {
|
||||
$callerNode = $callerNode->var;
|
||||
}
|
||||
}
|
||||
|
||||
$node->setAttribute(AttributeKey::METHOD_CALL_NODE_VARIABLE, $this->currentCaller);
|
||||
if ($callerNode instanceof StaticCall) {
|
||||
while ($callerNode instanceof StaticCall) {
|
||||
$callerNode = $callerNode->class;
|
||||
}
|
||||
}
|
||||
|
||||
$this->callerNode = $callerNode;
|
||||
$currentCallerName = $this->nodeNameResolver->getName($this->callerNode);
|
||||
|
||||
$node->setAttribute(AttributeKey::METHOD_CALL_NODE_CALLER_NAME, $currentCallerName);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\AssignManipulator;
|
||||
use PhpParser\Node\Identifier;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\MethodCallManipulator;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
@ -38,16 +38,20 @@ final class CreateMockToCreateStubRector extends AbstractRector
|
||||
new CodeSample(
|
||||
<<<'PHP'
|
||||
use PHPUnit\Framework\TestCase
|
||||
class MyTest extends TestCase {
|
||||
|
||||
class MyTest extends TestCase
|
||||
{
|
||||
public function testItBehavesAsExpected(): void
|
||||
{
|
||||
$stub = $this->createMock(\Exception::class);
|
||||
$stub->method('getMessage')
|
||||
->willReturn('a message');
|
||||
|
||||
$mock = $this->createMock(\Exception::class);
|
||||
$mock->expects($this->once())
|
||||
->method('getMessage')
|
||||
->willReturn('a message');
|
||||
|
||||
self::assertSame('a message', $stub->getMessage());
|
||||
self::assertSame('a message', $mock->getMessage());
|
||||
}
|
||||
@ -56,16 +60,20 @@ PHP
|
||||
,
|
||||
<<<'PHP'
|
||||
use PHPUnit\Framework\TestCase
|
||||
class MyTest extends TestCase {
|
||||
|
||||
class MyTest extends TestCase
|
||||
{
|
||||
public function testItBehavesAsExpected(): void
|
||||
{
|
||||
$stub = $this->createStub(\Exception::class);
|
||||
$stub->method('getMessage')
|
||||
->willReturn('a message');
|
||||
|
||||
$mock = $this->createMock(\Exception::class);
|
||||
$mock->expects($this->once())
|
||||
->method('getMessage')
|
||||
->willReturn('a message');
|
||||
|
||||
self::assertSame('a message', $stub->getMessage());
|
||||
self::assertSame('a message', $mock->getMessage());
|
||||
}
|
||||
@ -91,6 +99,7 @@ PHP
|
||||
if (! $this->isName($node->name, 'createMock')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
|
||||
if (! $parentNode instanceof Assign) {
|
||||
return null;
|
||||
@ -101,7 +110,12 @@ PHP
|
||||
return null;
|
||||
}
|
||||
|
||||
dump($this->methodCallManipulator->findMethodCallNamesOnVariable($mockVariable));
|
||||
$methodCallNamesOnVariable = $this->methodCallManipulator->findMethodCallNamesOnVariable($mockVariable);
|
||||
if (in_array('expects', $methodCallNamesOnVariable, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$node->name = new Identifier('createStub');
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\phpunit\Tests\Rector\MethodCall\CreateMockToCreateStubRector;
|
||||
namespace Rector\PHPUnit\Tests\Rector\MethodCall\CreateMockToCreateStubRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\phpunit\Rector\MethodCall\CreateMockToCreateStubRector;
|
||||
use Rector\PHPUnit\Rector\MethodCall\CreateMockToCreateStubRector;
|
||||
|
||||
final class CreateMockToCreateStubRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
|
@ -175,6 +175,24 @@ final class BetterNodeFinder
|
||||
return $this->findFirstPrevious($previousStatement, $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string[] $types
|
||||
*/
|
||||
public function findFirstPreviousOfTypes(Node $mainNode, array $types): ?Node
|
||||
{
|
||||
return $this->findFirstPrevious($mainNode, function (Node $node) use ($types) {
|
||||
foreach ($types as $type) {
|
||||
if (! is_a($node, $type, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node|Node[] $nodes
|
||||
*/
|
||||
|
@ -11,7 +11,6 @@ use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
@ -22,23 +21,14 @@ final class MethodCallManipulator
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var CallableNodeTraverser
|
||||
*/
|
||||
private $callableNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
CallableNodeTraverser $callableNodeTraverser,
|
||||
BetterNodeFinder $betterNodeFinder
|
||||
) {
|
||||
public function __construct(NodeNameResolver $nodeNameResolver, BetterNodeFinder $betterNodeFinder)
|
||||
{
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
}
|
||||
|
||||
@ -59,9 +49,6 @@ final class MethodCallManipulator
|
||||
$methodCallNamesOnVariable[] = $methodName;
|
||||
}
|
||||
|
||||
dump($methodCallNamesOnVariable);
|
||||
die;
|
||||
|
||||
return array_unique($methodCallNamesOnVariable);
|
||||
}
|
||||
|
||||
@ -120,31 +107,22 @@ final class MethodCallManipulator
|
||||
*/
|
||||
private function findMethodCallsOnVariable(Variable $variable): array
|
||||
{
|
||||
/** @var Node|null $parentNode */
|
||||
$parentNode = $variable->getAttribute(AttributeKey::PARENT_NODE);
|
||||
if ($parentNode === null) {
|
||||
// get scope node, e.g. parent function call, method call or anonymous function
|
||||
$scopeNode = $this->betterNodeFinder->findFirstPreviousOfTypes($variable, [FunctionLike::class]);
|
||||
if ($scopeNode === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$variableName = $this->nodeNameResolver->getName($variable);
|
||||
if ($variableName === null) {
|
||||
return [];
|
||||
}
|
||||
return $this->betterNodeFinder->find($scopeNode, function (Node $node) use ($variable) {
|
||||
if (! $node instanceof MethodCall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$previousMethodCalls = [];
|
||||
/** @var string $methodCallVariableName */
|
||||
$methodCallVariableName = $node->getAttribute(AttributeKey::METHOD_CALL_NODE_CALLER_NAME);
|
||||
|
||||
do {
|
||||
$methodCalls = $this->collectMethodCallsOnVariableName($parentNode, $variableName);
|
||||
$previousMethodCalls = array_merge($previousMethodCalls, $methodCalls);
|
||||
|
||||
$parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE);
|
||||
} while ($parentNode instanceof Node && ! $parentNode instanceof FunctionLike);
|
||||
|
||||
dump(12345);
|
||||
dump($previousMethodCalls);
|
||||
die;
|
||||
|
||||
return $previousMethodCalls;
|
||||
return $this->nodeNameResolver->isName($variable, $methodCallVariableName);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,79 +182,4 @@ final class MethodCallManipulator
|
||||
|
||||
return $parentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MethodCall[]
|
||||
*/
|
||||
private function collectMethodCallsOnVariableName(Node $node, string $variableName): array
|
||||
{
|
||||
$methodCalls = [];
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable($node, function (Node $node) use (
|
||||
$variableName,
|
||||
&$methodCalls
|
||||
) {
|
||||
// @todo add variable
|
||||
|
||||
if (! $node instanceof MethodCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
dump($node->getAttribute(AttributeKey::METHOD_CALL_NODE_VARIABLE));
|
||||
die;
|
||||
|
||||
// include chain method calls
|
||||
$nestedMethodCall = $node->var;
|
||||
|
||||
while ($nestedMethodCall instanceof MethodCall) {
|
||||
$onCalledVariable = $this->resolveMethodCallAndChainMethodCallVariable($nestedMethodCall);
|
||||
if (! $this->isVariableOfName($onCalledVariable, $variableName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodCalls[] = $nestedMethodCall;
|
||||
}
|
||||
|
||||
$onCalledVariable = $this->resolveMethodCallAndChainMethodCallVariable($node);
|
||||
dump($onCalledVariable);
|
||||
die;
|
||||
|
||||
if (! $onCalledVariable instanceof Variable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->nodeNameResolver->isName($onCalledVariable, $variableName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodCalls[] = $node;
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return $methodCalls;
|
||||
}
|
||||
|
||||
private function resolveMethodCallAndChainMethodCallVariable(MethodCall $methodCall): ?Variable
|
||||
{
|
||||
$possibleVariable = $methodCall->var;
|
||||
while ($possibleVariable instanceof MethodCall) {
|
||||
$possibleVariable = $possibleVariable->var;
|
||||
}
|
||||
|
||||
if (! $possibleVariable instanceof Variable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $possibleVariable;
|
||||
}
|
||||
|
||||
private function isVariableOfName(Node\Expr $node, string $variableName): bool
|
||||
{
|
||||
if (! $node instanceof Variable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->nodeNameResolver->isName($node, $variableName);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user