Updated Rector to commit e74ecfb6599f75dbd1463a6ac0b43d99c6dad466

e74ecfb659 [TypeDeclaration] Handle crash on func call not found on BoolReturnTypeFromBooleanStrictReturnsRector (#6327)
This commit is contained in:
Tomas Votruba 2024-09-26 08:25:20 +00:00
parent 5095dcdca5
commit 569cb9f7eb
18 changed files with 215 additions and 107 deletions

View File

@ -165,7 +165,11 @@ CODE_SAMPLE
if (!\is_string($functionName)) {
return \false;
}
$functionReflection = $this->reflectionProvider->getFunction(new Name($functionName), null);
$name = new Name($functionName);
if (!$this->reflectionProvider->hasFunction($name, null)) {
return \false;
}
$functionReflection = $this->reflectionProvider->getFunction($name, null);
if (!$functionReflection->isBuiltin()) {
return \false;
}

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '398b04db9fe8df75fd3b9d32805425a0b8a5ef6b';
public const PACKAGE_VERSION = 'e74ecfb6599f75dbd1463a6ac0b43d99c6dad466';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2024-09-23 14:12:46';
public const RELEASE_DATE = '2024-09-26 15:23:06';
/**
* @var int
*/

View File

@ -53,6 +53,7 @@ return array(
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTextNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTextNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PropertyTagValueNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PureUnlessCallableIsImpureTagValueNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/PureUnlessCallableIsImpureTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireExtendsTagValueNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireExtendsTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireImplementsTagValueNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireImplementsTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ReturnTagValueNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php',

View File

@ -272,6 +272,7 @@ class ComposerStaticInitb7f7ba4346a5afcc49f848aa6217e771
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTextNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTextNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PropertyTagValueNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PureUnlessCallableIsImpureTagValueNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/PureUnlessCallableIsImpureTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireExtendsTagValueNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireExtendsTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\RequireImplementsTagValueNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireImplementsTagValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\ReturnTagValueNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php',

View File

@ -512,17 +512,17 @@
},
{
"name": "illuminate\/container",
"version": "v11.23.5",
"version_normalized": "11.23.5.0",
"version": "v11.24.1",
"version_normalized": "11.24.1.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/illuminate\/container.git",
"reference": "66d20471c8c55ef056044dc1ff16da90d7b4d649"
"reference": "d6aca7c315d68cb6807c139facd7ea134b4f5104"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/illuminate\/container\/zipball\/66d20471c8c55ef056044dc1ff16da90d7b4d649",
"reference": "66d20471c8c55ef056044dc1ff16da90d7b4d649",
"url": "https:\/\/api.github.com\/repos\/illuminate\/container\/zipball\/d6aca7c315d68cb6807c139facd7ea134b4f5104",
"reference": "d6aca7c315d68cb6807c139facd7ea134b4f5104",
"shasum": ""
},
"require": {
@ -533,7 +533,7 @@
"provide": {
"psr\/container-implementation": "1.1|2.0"
},
"time": "2024-09-11T20:15:17+00:00",
"time": "2024-09-20T12:51:05+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -569,17 +569,17 @@
},
{
"name": "illuminate\/contracts",
"version": "v11.23.5",
"version_normalized": "11.23.5.0",
"version": "v11.24.1",
"version_normalized": "11.24.1.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/illuminate\/contracts.git",
"reference": "5a4c6dcf633c1f69e1b70bbea1ef1b7d2186d3da"
"reference": "56312862af937bd6da8e6dc8bbd88188dfb478f8"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/illuminate\/contracts\/zipball\/5a4c6dcf633c1f69e1b70bbea1ef1b7d2186d3da",
"reference": "5a4c6dcf633c1f69e1b70bbea1ef1b7d2186d3da",
"url": "https:\/\/api.github.com\/repos\/illuminate\/contracts\/zipball\/56312862af937bd6da8e6dc8bbd88188dfb478f8",
"reference": "56312862af937bd6da8e6dc8bbd88188dfb478f8",
"shasum": ""
},
"require": {
@ -587,7 +587,7 @@
"psr\/container": "^1.1.1|^2.0.1",
"psr\/simple-cache": "^1.0|^2.0|^3.0"
},
"time": "2024-09-12T15:25:08+00:00",
"time": "2024-09-22T15:08:08+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -867,17 +867,17 @@
},
{
"name": "phpstan\/phpdoc-parser",
"version": "1.31.0",
"version_normalized": "1.31.0.0",
"version": "1.32.0",
"version_normalized": "1.32.0.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/phpstan\/phpdoc-parser.git",
"reference": "249f15fb843bf240cf058372dad29e100cee6c17"
"reference": "6ca22b154efdd9e3c68c56f5d94670920a1c19a4"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpdoc-parser\/zipball\/249f15fb843bf240cf058372dad29e100cee6c17",
"reference": "249f15fb843bf240cf058372dad29e100cee6c17",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpdoc-parser\/zipball\/6ca22b154efdd9e3c68c56f5d94670920a1c19a4",
"reference": "6ca22b154efdd9e3c68c56f5d94670920a1c19a4",
"shasum": ""
},
"require": {
@ -894,7 +894,7 @@
"phpunit\/phpunit": "^9.5",
"symfony\/process": "^5.2"
},
"time": "2024-09-22T11:32:18+00:00",
"time": "2024-09-26T07:23:32+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -911,7 +911,7 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https:\/\/github.com\/phpstan\/phpdoc-parser\/issues",
"source": "https:\/\/github.com\/phpstan\/phpdoc-parser\/tree\/1.31.0"
"source": "https:\/\/github.com\/phpstan\/phpdoc-parser\/tree\/1.32.0"
},
"install-path": "..\/phpstan\/phpdoc-parser"
},
@ -1811,12 +1811,12 @@
"source": {
"type": "git",
"url": "https:\/\/github.com\/rectorphp\/rector-phpunit.git",
"reference": "bffd16339732095ba84bf21475eac749c1c9a154"
"reference": "6a33c23cc196081d405cb38cd70af860fc796884"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-phpunit\/zipball\/bffd16339732095ba84bf21475eac749c1c9a154",
"reference": "bffd16339732095ba84bf21475eac749c1c9a154",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-phpunit\/zipball\/6a33c23cc196081d405cb38cd70af860fc796884",
"reference": "6a33c23cc196081d405cb38cd70af860fc796884",
"shasum": ""
},
"require": {
@ -1839,7 +1839,7 @@
"tomasvotruba\/class-leak": "^0.2",
"tracy\/tracy": "^2.10"
},
"time": "2024-09-22T19:04:35+00:00",
"time": "2024-09-26T07:47:53+00:00",
"default-branch": true,
"type": "rector-extension",
"extra": {

File diff suppressed because one or more lines are too long

View File

@ -927,6 +927,9 @@ class Container implements ArrayAccess, ContainerContract
if ($parameter->isVariadic()) {
return [];
}
if ($parameter->hasType() && $parameter->allowsNull()) {
return null;
}
$this->unresolvablePrimitive($parameter);
}
/**

View File

@ -3,7 +3,7 @@
namespace RectorPrefix202409\Illuminate\Contracts\Concurrency;
use Closure;
use RectorPrefix202409\Illuminate\Foundation\Defer\DeferredCallback;
use RectorPrefix202409\Illuminate\Support\Defer\DeferredCallback;
interface Driver
{
/**

View File

@ -11,7 +11,7 @@ interface SerializesCastableAttributes
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @param array<string, mixed> $attributes
* @return mixed
*/
public function serialize(Model $model, string $key, $value, array $attributes);

View File

@ -13,7 +13,7 @@ interface InvokableRule
*
* @param string $attribute
* @param mixed $value
* @param \Closure(string, ?string = null): \Illuminate\Translation\PotentiallyTranslatedString $fail
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
* @return void
*/
public function __invoke(string $attribute, $value, Closure $fail);

View File

@ -10,7 +10,7 @@ interface ValidationRule
*
* @param string $attribute
* @param mixed $value
* @param \Closure(string, ?string = null): \Illuminate\Translation\PotentiallyTranslatedString $fail
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
* @return void
*/
public function validate(string $attribute, $value, Closure $fail) : void;

View File

@ -93,6 +93,15 @@ class PhpDocNode implements Node
return $value instanceof \PHPStan\PhpDocParser\Ast\PhpDoc\ParamClosureThisTagValueNode;
});
}
/**
* @return PureUnlessCallableIsImpureTagValueNode[]
*/
public function getPureUnlessCallableIsImpureTagValues(string $tagName = '@pure-unless-callable-is-impure') : array
{
return array_filter(array_column($this->getTagsByName($tagName), 'value'), static function (\PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode $value) : bool {
return $value instanceof \PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode;
});
}
/**
* @return TemplateTagValueNode[]
*/

View File

@ -0,0 +1,24 @@
<?php
declare (strict_types=1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function trim;
class PureUnlessCallableIsImpureTagValueNode implements \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode
{
use NodeAttributes;
/** @var string */
public $parameterName;
/** @var string (may be empty) */
public $description;
public function __construct(string $parameterName, string $description)
{
$this->parameterName = $parameterName;
$this->description = $description;
}
public function __toString() : string
{
return trim("{$this->parameterName} {$this->description}");
}
}

View File

@ -297,6 +297,10 @@ class PhpDocParser
case '@phpstan-param-closure-this':
$tagValue = $this->parseParamClosureThisTagValue($tokens);
break;
case '@pure-unless-callable-is-impure':
case '@phpstan-pure-unless-callable-is-impure':
$tagValue = $this->parsePureUnlessCallableIsImpureTagValue($tokens);
break;
case '@var':
case '@phpstan-var':
case '@psalm-var':
@ -636,6 +640,12 @@ class PhpDocParser
$description = $this->parseOptionalDescription($tokens);
return new Ast\PhpDoc\ParamClosureThisTagValueNode($type, $parameterName, $description);
}
private function parsePureUnlessCallableIsImpureTagValue(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens) : Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode
{
$parameterName = $this->parseRequiredVariableName($tokens);
$description = $this->parseOptionalDescription($tokens);
return new Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode($parameterName, $description);
}
private function parseVarTagValue(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens) : Ast\PhpDoc\VarTagValueNode
{
$type = $this->typeParser->parse($tokens);

View File

@ -32,6 +32,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\RequireExtendsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\RequireImplementsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
@ -249,6 +250,9 @@ final class Printer
if ($node instanceof ParamClosureThisTagValueNode) {
return trim("{$node->type} {$node->parameterName} {$node->description}");
}
if ($node instanceof PureUnlessCallableIsImpureTagValueNode) {
return trim("{$node->parameterName} {$node->description}");
}
if ($node instanceof PropertyTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->propertyName} {$node->description}");

View File

@ -9,7 +9,7 @@ namespace Rector\RectorInstaller;
*/
final class GeneratedConfig
{
public const EXTENSIONS = array('rector/rector-doctrine' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-doctrine', 'relative_install_path' => '../../rector-doctrine', 'extra' => NULL, 'version' => 'dev-main e75008c'), 'rector/rector-downgrade-php' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-downgrade-php', 'relative_install_path' => '../../rector-downgrade-php', 'extra' => NULL, 'version' => 'dev-main cfebdb1'), 'rector/rector-phpunit' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-phpunit', 'relative_install_path' => '../../rector-phpunit', 'extra' => NULL, 'version' => 'dev-main bffd163'), 'rector/rector-symfony' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-symfony', 'relative_install_path' => '../../rector-symfony', 'extra' => NULL, 'version' => 'dev-main 352a84c'));
public const EXTENSIONS = array('rector/rector-doctrine' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-doctrine', 'relative_install_path' => '../../rector-doctrine', 'extra' => NULL, 'version' => 'dev-main e75008c'), 'rector/rector-downgrade-php' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-downgrade-php', 'relative_install_path' => '../../rector-downgrade-php', 'extra' => NULL, 'version' => 'dev-main cfebdb1'), 'rector/rector-phpunit' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-phpunit', 'relative_install_path' => '../../rector-phpunit', 'extra' => NULL, 'version' => 'dev-main 6a33c23'), 'rector/rector-symfony' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-symfony', 'relative_install_path' => '../../rector-symfony', 'extra' => NULL, 'version' => 'dev-main 352a84c'));
private function __construct()
{
}

View File

@ -31,7 +31,7 @@ use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\PHPUnit\Tests\Rector\StmtsAwareInterface\WithConsecutiveRector\WithConsecutiveRectorTest
* @see \Rector\PHPUnit\Tests\PHPUnit100\Rector\StmtsAwareInterface\WithConsecutiveRector\WithConsecutiveRectorTest
*/
final class WithConsecutiveRector extends AbstractRector implements MinPhpVersionInterface
{
@ -122,28 +122,16 @@ CODE_SAMPLE
if ($this->hasWillReturnMapOrWill($node)) {
return null;
}
$firstArg = $withConsecutiveMethodCall->getArgs()[0];
$isWithConsecutiveVariadic = $firstArg->unpack;
$returnStmts = [];
$willReturn = $this->findMethodCall($node, 'willReturn');
if ($willReturn instanceof MethodCall) {
$args = $willReturn->getArgs();
if (\count($args) !== 1 || !$args[0] instanceof Arg) {
return null;
}
$returnStmts = [new Return_($args[0]->value)];
$returnStmts[] = $this->createWillReturnStmt($willReturn);
}
$willReturnSelf = $this->findMethodCall($node, 'willReturnSelf');
if ($willReturnSelf instanceof MethodCall) {
if ($returnStmts !== []) {
return null;
}
$selfVariable = $willReturnSelf;
while (\true) {
if (!$selfVariable instanceof MethodCall) {
break;
}
$selfVariable = $selfVariable->var;
}
$returnStmts = [new Return_($selfVariable)];
$returnStmts[] = $this->createWillReturnSelfStmts($willReturnSelf);
}
$willReturnArgument = $this->findMethodCall($node, 'willReturnArgument');
if ($willReturnArgument instanceof MethodCall) {
@ -151,11 +139,11 @@ CODE_SAMPLE
return null;
}
$parametersVariable = new Variable('parameters');
$args = $willReturnArgument->getArgs();
if (\count($args) !== 1 || !$args[0] instanceof Arg) {
$firstArgs = $willReturnArgument->getArgs()[0];
if (!$firstArgs instanceof Arg) {
return null;
}
$returnStmts = [new Return_(new ArrayDimFetch($parametersVariable, $args[0]->value))];
$returnStmts = [new Return_(new ArrayDimFetch($parametersVariable, $firstArgs->value))];
}
$willReturnOnConsecutiveCallsArgument = $this->findMethodCall($node, 'willReturnOnConsecutiveCalls');
if ($willReturnOnConsecutiveCallsArgument instanceof MethodCall) {
@ -176,11 +164,11 @@ CODE_SAMPLE
if ($returnStmts !== []) {
return null;
}
$args = $willReturnReferenceArgument->args;
if (\count($args) !== 1 || !$args[0] instanceof Arg) {
$firstArg = $willReturnReferenceArgument->getArgs()[0] ?? null;
if (!$firstArg instanceof Arg) {
return null;
}
$referenceVariable = $args[0]->value;
$referenceVariable = $firstArg->value;
if (!$referenceVariable instanceof Variable) {
return null;
}
@ -191,11 +179,11 @@ CODE_SAMPLE
if ($returnStmts !== []) {
return null;
}
$args = $willThrowException->getArgs();
if (\count($args) !== 1 || !$args[0] instanceof Arg) {
$firstArg = $willThrowException->getArgs()[0] ?? null;
if (!$firstArg instanceof Arg) {
return null;
}
$returnStmts = [new Throw_($args[0]->value)];
$returnStmts = [new Throw_($firstArg->value)];
}
$this->removeMethodCalls($node, ['willReturn', 'willReturnArgument', 'willReturnSelf', 'willReturnOnConsecutiveCalls', 'willReturnReference', 'willThrowException']);
$expectsCall = $this->matchAndRefactorExpectsMethodCall($node);
@ -207,31 +195,15 @@ CODE_SAMPLE
// 2. does willReturnCallback() exist? just merge
$existingWillReturnCallback = $this->findMethodCall($node, 'willReturnCallback');
if ($existingWillReturnCallback instanceof MethodCall) {
$callbackArg = $existingWillReturnCallback->getArgs()[0];
if (!$callbackArg->value instanceof Closure) {
throw new ShouldNotHappenException();
}
$callbackClosure = $callbackArg->value;
$matcherVariable = new Variable('matcher');
$parametersVariable = new Variable('parameters');
$parametersMatch = $this->withConsecutiveMatchFactory->createParametersMatch($matcherVariable, $withConsecutiveMethodCall, $parametersVariable);
$callbackClosure->params[] = new Param($parametersVariable);
$callbackClosure->stmts = \array_merge([new Expression($parametersMatch)], $callbackClosure->stmts);
$this->removeMethodCalls($node, [self::WITH_CONSECUTIVE_METHOD]);
return [$node];
return $this->refactorWithExistingWillReturnCallback($existingWillReturnCallback, $withConsecutiveMethodCall, $node);
}
// 3. rename and replace withConsecutive()
return $this->refactorToWillReturnCallback($withConsecutiveMethodCall, $returnStmts, $referenceVariable, $expectsCall, $node);
return $this->refactorToWillReturnCallback($withConsecutiveMethodCall, $returnStmts, $referenceVariable, $expectsCall, $node, $isWithConsecutiveVariadic);
}
public function provideMinPhpVersion() : int
{
/**
* This rule just work for phpunit 10,
* And as php 8.1 is the min version supported by phpunit 10, then we decided to let this version as minimum.
*
* You can see more detail in this issue: https://github.com/rectorphp/rector-phpunit/issues/272
*/
return PhpVersion::PHP_81;
// This rule uses PHP 8.0 match
return PhpVersion::PHP_80;
}
/**
* Replace $this->expects(...)
@ -284,6 +256,41 @@ CODE_SAMPLE
});
return $nodesWithWillReturnMap !== [];
}
/**
* @param Stmt[] $returnStmts
* @return Stmt[]
* @param \PhpParser\Node\Expr|\PhpParser\Node\Expr\Variable|null $referenceVariable
* @param \PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\MethodCall $expectsCall
*/
private function refactorToWillReturnCallback(MethodCall $withConsecutiveMethodCall, array $returnStmts, $referenceVariable, $expectsCall, Expression $expression, bool $isWithConsecutiveVariadic) : array
{
$withConsecutiveMethodCall->name = new Identifier('willReturnCallback');
$withConsecutiveMethodCall->args = [new Arg($this->withConsecutiveMatchFactory->createClosure($withConsecutiveMethodCall, $returnStmts, $referenceVariable, $isWithConsecutiveVariadic))];
$hasExpects = $this->findMethodCall($expression, 'expects') instanceof MethodCall;
$matcherVariable = new Variable('matcher');
if ($hasExpects === \false) {
/** @var MethodCall $mockMethodCall */
$mockMethodCall = $expression->expr;
$mockMethodCall->var = new MethodCall($mockMethodCall->var, 'expects', [new Arg($matcherVariable)]);
}
$matcherAssign = new Assign($matcherVariable, $expectsCall);
return [new Expression($matcherAssign), $expression];
}
private function refactorWithExistingWillReturnCallback(MethodCall $existingWillReturnCallback, MethodCall $withConsecutiveMethodCall, Expression $expression) : Expression
{
$callbackArg = $existingWillReturnCallback->getArgs()[0];
if (!$callbackArg->value instanceof Closure) {
throw new ShouldNotHappenException();
}
$callbackClosure = $callbackArg->value;
$matcherVariable = new Variable('matcher');
$parametersVariable = new Variable('parameters');
$parametersMatch = $this->withConsecutiveMatchFactory->createParametersMatch($matcherVariable, $withConsecutiveMethodCall, $parametersVariable);
$callbackClosure->params[] = new Param($parametersVariable);
$callbackClosure->stmts = \array_merge([new Expression($parametersMatch)], $callbackClosure->stmts);
$this->removeMethodCalls($expression, [self::WITH_CONSECUTIVE_METHOD]);
return $expression;
}
/**
* @param string[] $methodNames
*/
@ -299,17 +306,23 @@ CODE_SAMPLE
return $node->var;
});
}
/**
* @param Stmt[] $returnStmts
* @return Stmt[]
* @param \PhpParser\Node\Expr|\PhpParser\Node\Expr\Variable|null $referenceVariable
* @param \PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\MethodCall $expectsCall
*/
private function refactorToWillReturnCallback(MethodCall $withConsecutiveMethodCall, array $returnStmts, $referenceVariable, $expectsCall, Expression $expression) : array
private function createWillReturnStmt(MethodCall $willReturnMethodCall) : Return_
{
$withConsecutiveMethodCall->name = new Identifier('willReturnCallback');
$withConsecutiveMethodCall->args = [new Arg($this->withConsecutiveMatchFactory->createClosure($withConsecutiveMethodCall, $returnStmts, $referenceVariable))];
$matcherAssign = new Assign(new Variable('matcher'), $expectsCall);
return [new Expression($matcherAssign), $expression];
$firstArg = $willReturnMethodCall->getArgs()[0] ?? null;
if (!$firstArg instanceof Arg) {
throw new ShouldNotHappenException();
}
return new Return_($firstArg->value);
}
private function createWillReturnSelfStmts(MethodCall $willReturnSelfMethodCall) : Return_
{
$selfVariable = $willReturnSelfMethodCall;
while (\true) {
if (!$selfVariable instanceof MethodCall) {
break;
}
$selfVariable = $selfVariable->var;
}
return new Return_($selfVariable);
}
}

View File

@ -5,7 +5,10 @@ namespace Rector\PHPUnit\NodeFactory;
use PhpParser\BuilderFactory;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\BinaryOp\Minus;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\ClosureUse;
use PhpParser\Node\Expr\Match_;
@ -46,31 +49,32 @@ final class WithConsecutiveMatchFactory
* @param Stmt[] $returnStmts
* @param \PhpParser\Node\Expr\Variable|\PhpParser\Node\Expr|null $referenceVariable
*/
public function createClosure(MethodCall $expectsMethodCall, array $returnStmts, $referenceVariable) : Closure
public function createClosure(MethodCall $withConsecutiveMethodCall, array $returnStmts, $referenceVariable, bool $isWithConsecutiveVariadic) : Closure
{
$byRef = $referenceVariable instanceof Variable;
$closure = new Closure(['byRef' => $byRef]);
$matcherVariable = new Variable('matcher');
$closure->uses[] = new ClosureUse($matcherVariable);
$usedVariables = $this->resolveUniqueUsedVariables(\array_merge($expectsMethodCall->getArgs(), $this->resolveUniqueUsedVariables($returnStmts)));
foreach ($usedVariables as $usedVariable) {
$closureUse = new ClosureUse($usedVariable);
if ($byRef && $this->nodeNameResolver->areNamesEqual($usedVariable, $referenceVariable)) {
$closureUse->byRef = \true;
}
$closure->uses[] = $closureUse;
}
$usedVariables = $this->resolveUsedVariables($withConsecutiveMethodCall, $returnStmts);
$isByRef = $this->isByRef($referenceVariable);
$uses = $this->createUses($matcherVariable, $usedVariables);
$parametersVariable = new Variable('parameters');
$match = $this->createParametersMatch($matcherVariable, $expectsMethodCall, $parametersVariable);
$closure->params[] = new Param($parametersVariable);
$closure->stmts = \array_merge([new Expression($match)], $returnStmts);
return $closure;
$match = $this->createParametersMatch($matcherVariable, $withConsecutiveMethodCall, $parametersVariable);
$parametersParam = new Param($parametersVariable);
if ($isWithConsecutiveVariadic) {
$parametersParam->variadic = \true;
}
return new Closure(['byRef' => $isByRef, 'uses' => $uses, 'params' => [$parametersParam], 'stmts' => \array_merge([new Expression($match)], $returnStmts)]);
}
public function createParametersMatch(Variable $matcherVariable, MethodCall $expectsMethodCall, Variable $parameters) : Match_
/**
* @return \PhpParser\Node\Expr\Match_|\PhpParser\Node\Expr\MethodCall
*/
public function createParametersMatch(Variable $matcherVariable, MethodCall $withConsecutiveMethodCall, Variable $parameters)
{
$firstArg = $withConsecutiveMethodCall->getArgs()[0] ?? null;
if ($firstArg instanceof Arg && $firstArg->unpack) {
return $this->createAssertSameDimFetch($firstArg, $matcherVariable, $parameters);
}
$numberOfInvocationsMethodCall = new MethodCall($matcherVariable, new Identifier('numberOfInvocations'));
$matchArms = [];
foreach ($expectsMethodCall->getArgs() as $key => $arg) {
foreach ($withConsecutiveMethodCall->getArgs() as $key => $arg) {
$assertEquals = $this->builderFactory->staticCall('self', 'assertEquals', [$arg, $parameters]);
$matchArms[] = new MatchArm([new LNumber($key + 1)], $assertEquals);
}
@ -94,4 +98,39 @@ final class WithConsecutiveMatchFactory
}
return $uniqueUsedVariables;
}
private function createAssertSameDimFetch(Arg $firstArg, Variable $matcherVariable, Variable $parameters) : MethodCall
{
$currentValueArrayDimFetch = new ArrayDimFetch($firstArg->value, new Minus(new MethodCall($matcherVariable, new Identifier('numberOfInvocations')), new LNumber(1)));
$compareArgs = [new Arg($currentValueArrayDimFetch), new Arg(new ArrayDimFetch($parameters, new LNumber(0)))];
return $this->builderFactory->methodCall(new Variable('this'), 'assertSame', $compareArgs);
}
/**
* @param Stmt[] $returnStmts
* @return Variable[]
*/
private function resolveUsedVariables(MethodCall $withConsecutiveMethodCall, array $returnStmts) : array
{
$consecutiveArgs = $withConsecutiveMethodCall->getArgs();
$stmtVariables = $this->resolveUniqueUsedVariables($returnStmts);
return $this->resolveUniqueUsedVariables(\array_merge($consecutiveArgs, $stmtVariables));
}
/**
* @param \PhpParser\Node\Expr|\PhpParser\Node\Expr\Variable|null $referenceVariable
*/
private function isByRef($referenceVariable) : bool
{
return $referenceVariable instanceof Variable;
}
/**
* @param Variable[] $usedVariables
* @return ClosureUse[]
*/
private function createUses(Variable $matcherVariable, array $usedVariables) : array
{
$uses = [new ClosureUse($matcherVariable)];
foreach ($usedVariables as $usedVariable) {
$uses[] = new ClosureUse($usedVariable);
}
return $uses;
}
}