diff --git a/packages/vendor-locker/src/VendorFileDetector.php b/packages/vendor-locker/src/VendorFileDetector.php deleted file mode 100644 index 726c42b019e3..000000000000 --- a/packages/vendor-locker/src/VendorFileDetector.php +++ /dev/null @@ -1,86 +0,0 @@ -getClassFileNameByClassMethod($node) - : $this->getClassFileNameByMethodCall($node); - - if ($fileName === null) { - return false; - } - - return Strings::contains($fileName, 'vendor'); - } - - private function getClassFileNameByClassMethod(ClassMethod $classMethod): ?string - { - $parent = $classMethod->getAttribute(AttributeKey::PARENT_NODE); - if (! $parent instanceof Class_) { - return null; - } - - $shortClassName = (string) $parent->name; - if (Strings::match($shortClassName, self::ANONYMOUS_CLASS_REGEX)) { - return null; - } - - $reflectionClass = new ReflectionClass((string) $parent->namespacedName); - return (string) $reflectionClass->getFileName(); - } - - private function getClassFileNameByMethodCall(MethodCall $methodCall): ?string - { - $scope = $methodCall->getAttribute(AttributeKey::SCOPE); - if ($scope === null) { - return null; - } - - $type = $scope->getType($methodCall->var); - if ($type instanceof ObjectType) { - $classReflection = $type->getClassReflection(); - if ($classReflection === null) { - return null; - } - - return (string) $classReflection->getFileName(); - } - - if ($type instanceof ThisType) { - $staticObjectType = $type->getStaticObjectType(); - $classReflection = $staticObjectType->getClassReflection(); - if ($classReflection === null) { - return null; - } - - return (string) $classReflection->getFileName(); - } - - return null; - } -} diff --git a/rules/coding-style/src/NodeAnalyzer/SpreadVariablesCollector.php b/rules/coding-style/src/NodeAnalyzer/SpreadVariablesCollector.php index d41f5f85a90e..b950919f45ed 100644 --- a/rules/coding-style/src/NodeAnalyzer/SpreadVariablesCollector.php +++ b/rules/coding-style/src/NodeAnalyzer/SpreadVariablesCollector.php @@ -4,49 +4,33 @@ namespace Rector\CodingStyle\NodeAnalyzer; -use PhpParser\Node; -use PhpParser\Node\Arg; -use PhpParser\Node\Expr\Variable; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\ClassMethod; +use Rector\NodeTypeResolver\Node\AttributeKey; final class SpreadVariablesCollector { /** - * @param Param[]|Arg[] $nodes - * @return Param[]|Arg[] + * @return array */ - public function getSpreadVariables(array $nodes): array + public function resolveFromClassMethod(ClassMethod $classMethod): array { - $spreadVariables = []; + $spreadParams = []; - foreach ($nodes as $key => $paramOrArg) { - if ($this->isVariadicParam($paramOrArg)) { - $spreadVariables[$key] = $paramOrArg; + foreach ($classMethod->params as $key => $param) { + // prevent race-condition removal on class method + $originalParam = $param->getAttribute(AttributeKey::ORIGINAL_NODE); + if (! $originalParam instanceof Param) { + continue; } - if ($this->isVariadicArg($paramOrArg)) { - $spreadVariables[$key] = $paramOrArg; + if (! $originalParam->variadic) { + continue; } - } - - return $spreadVariables; - } - private function isVariadicParam(Node $node): bool - { - if (! $node instanceof Param) { - return false; - } - - return $node->variadic && $node->type === null; - } - - private function isVariadicArg(Node $node): bool - { - if (! $node instanceof Arg) { - return false; + $spreadParams[$key] = $param; } - return $node->unpack && $node->value instanceof Variable; + return $spreadParams; } } diff --git a/rules/coding-style/src/Rector/ClassMethod/UnSpreadOperatorRector.php b/rules/coding-style/src/Rector/ClassMethod/UnSpreadOperatorRector.php index 76dd8172b5c9..11a4fddc96be 100644 --- a/rules/coding-style/src/Rector/ClassMethod/UnSpreadOperatorRector.php +++ b/rules/coding-style/src/Rector/ClassMethod/UnSpreadOperatorRector.php @@ -5,13 +5,12 @@ namespace Rector\CodingStyle\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Identifier; -use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; use Rector\CodingStyle\NodeAnalyzer\SpreadVariablesCollector; use Rector\Core\Rector\AbstractRector; -use Rector\VendorLocker\VendorFileDetector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,21 +19,13 @@ */ final class UnSpreadOperatorRector extends AbstractRector { - /** - * @var VendorFileDetector - */ - private $vendorFileDetector; - /** * @var SpreadVariablesCollector */ private $spreadVariablesCollector; - public function __construct( - VendorFileDetector $vendorFileDetector, - SpreadVariablesCollector $spreadVariablesCollector - ) { - $this->vendorFileDetector = $vendorFileDetector; + public function __construct(SpreadVariablesCollector $spreadVariablesCollector) + { $this->spreadVariablesCollector = $spreadVariablesCollector; } @@ -95,23 +86,14 @@ public function refactor(Node $node): ?Node private function processUnspreadOperatorClassMethodParams(ClassMethod $classMethod): ?ClassMethod { - if ($this->vendorFileDetector->isNodeInVendor($classMethod)) { + $spreadParams = $this->spreadVariablesCollector->resolveFromClassMethod($classMethod); + if ($spreadParams === []) { return null; } - $params = $classMethod->params; - if ($params === []) { - return null; - } - - $spreadVariables = $this->spreadVariablesCollector->getSpreadVariables($params); - if ($spreadVariables === []) { - return null; - } - - foreach (array_keys($spreadVariables) as $key) { - $classMethod->params[$key]->variadic = false; - $classMethod->params[$key]->type = new Identifier('array'); + foreach ($spreadParams as $spreadParam) { + $spreadParam->variadic = false; + $spreadParam->type = new Identifier('array'); } return $classMethod; @@ -119,24 +101,53 @@ private function processUnspreadOperatorClassMethodParams(ClassMethod $classMeth private function processUnspreadOperatorMethodCallArgs(MethodCall $methodCall): ?MethodCall { - if ($this->vendorFileDetector->isNodeInVendor($methodCall)) { + $classMethod = $this->nodeRepository->findClassMethodByMethodCall($methodCall); + if ($classMethod === null) { return null; } - $args = $methodCall->args; - if ($args === []) { + $spreadParams = $this->spreadVariablesCollector->resolveFromClassMethod($classMethod); + if ($spreadParams === []) { return null; } - $spreadVariables = $this->spreadVariablesCollector->getSpreadVariables($args); - if ($spreadVariables === []) { - return null; + $firstSpreadParamPosition = array_key_first($spreadParams); + $variadicArgs = $this->resolveVariadicArgsByVariadicParams($methodCall, $firstSpreadParamPosition); + + $hasUnpacked = false; + + foreach ($variadicArgs as $position => $variadicArg) { + if ($variadicArg->unpack) { + $variadicArg->unpack = null; + $hasUnpacked = true; + $methodCall->args[$position] = $variadicArg; + } } - foreach (array_keys($spreadVariables) as $key) { - $methodCall->args[$key]->unpack = false; + if ($hasUnpacked) { + return $methodCall; } + $methodCall->args[$firstSpreadParamPosition] = new Arg($this->createArray($variadicArgs)); return $methodCall; } + + /** + * @return Arg[] + */ + private function resolveVariadicArgsByVariadicParams(MethodCall $methodCall, int $firstSpreadParamPosition): array + { + $variadicArgs = []; + + foreach ($methodCall->args as $position => $arg) { + if ($position < $firstSpreadParamPosition) { + continue; + } + + $variadicArgs[] = $arg; + unset($methodCall->args[$position]); + } + + return $variadicArgs; + } } diff --git a/rules/coding-style/tests/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_typed_param_variadic.php.inc b/rules/coding-style/tests/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_typed_param_variadic.php.inc deleted file mode 100644 index fa432469fc51..000000000000 --- a/rules/coding-style/tests/Rector/ClassMethod/UnSpreadOperatorRector/Fixture/skip_typed_param_variadic.php.inc +++ /dev/null @@ -1,10 +0,0 @@ - +----- + diff --git a/src/PhpParser/Node/NodeFactory.php b/src/PhpParser/Node/NodeFactory.php index 2c144721dfcf..ffdeeefbaa72 100644 --- a/src/PhpParser/Node/NodeFactory.php +++ b/src/PhpParser/Node/NodeFactory.php @@ -531,6 +531,10 @@ private function createArrayItem($item, $key = null): ArrayItem return new ArrayItem($itemValue); } + if ($item instanceof Arg) { + return new ArrayItem($item->value); + } + throw new NotImplementedException(sprintf( 'Not implemented yet. Go to "%s()" and add check for "%s" node.', __METHOD__,