diff --git a/phpstan.neon b/phpstan.neon index 93f003793aa..aed17b5168d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -253,7 +253,6 @@ parameters: identifier: return.type paths: - src/NodeTypeResolver/PHPStan/Type/TypeFactory.php - - rules/Arguments/ArgumentDefaultValueReplacer.php # from mapper interface - diff --git a/rules/Arguments/ArgumentDefaultValueReplacer.php b/rules/Arguments/ArgumentDefaultValueReplacer.php deleted file mode 100644 index 579b7899c72..00000000000 --- a/rules/Arguments/ArgumentDefaultValueReplacer.php +++ /dev/null @@ -1,287 +0,0 @@ -params[$replaceArgumentDefaultValue->getPosition()])) { - return null; - } - - return $this->processParams($node, $replaceArgumentDefaultValue); - } - - return $this->processArgs($node, $replaceArgumentDefaultValue); - } - - private function isDefaultValueMatched(?Expr $expr, mixed $value): bool - { - // allow any values before, also allow param without default value - if ($value === ReplaceArgumentDefaultValueInterface::ANY_VALUE_BEFORE) { - return true; - } - - if (! $expr instanceof Expr) { - return false; - } - - if ($this->valueResolver->isValue($expr, $value)) { - return true; - } - - // ValueResolver::isValue returns false when default value is `null` - return $value === null && $this->valueResolver->isNull($expr); - } - - private function processParams( - ClassMethod $classMethod, - ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue - ): ?ClassMethod { - $position = $replaceArgumentDefaultValue->getPosition(); - - if (! $this->isDefaultValueMatched( - $classMethod->params[$position]->default, - $replaceArgumentDefaultValue->getValueBefore() - )) { - return null; - } - - $classMethod->params[$position]->default = $this->normalizeValue($replaceArgumentDefaultValue->getValueAfter()); - return $classMethod; - } - - /** - * @template TCall as (MethodCall|StaticCall|FuncCall|New_) - * - * @param TCall $expr - * @return TCall|null - */ - private function processArgs( - MethodCall|StaticCall|FuncCall|New_ $expr, - ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue - ): MethodCall|StaticCall|FuncCall|New_|null { - if ($expr->isFirstClassCallable()) { - return null; - } - - $position = $replaceArgumentDefaultValue->getPosition(); - $arguments = $expr->getArgs(); - $firstNamedArgPosition = $this->argsAnalyzer->resolveFirstNamedArgPosition($arguments); - // if the call has named argyments and we want to replace an array of values - // we cannot replace it as we cannot really match this array of values to - // the existing arguments, it would be too complex - if ($firstNamedArgPosition !== null && is_array($replaceArgumentDefaultValue->getValueBefore())) { - return null; - } - - // if the call has named arguments and the argument that we want to replace is not - // before any named argument, we need to check if it is in the list of named arguments - // if it is, we use the position of the named argyment as the position to replace - // if it is not, we cannot replace it - if ($firstNamedArgPosition !== null && $position >= $firstNamedArgPosition) { - $reflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($expr); - if (! $reflection instanceof MethodReflection && ! $reflection instanceof FunctionReflection) { - return null; - } - - $parametersAcceptor = ParametersAcceptorSelector::combineAcceptors($reflection->getVariants()); - $parameterReflection = $parametersAcceptor->getParameters()[$position] ?? null; - - $newPosition = -1; - if ($parameterReflection !== null) { - $newPosition = $this->argsAnalyzer->resolveArgPosition( - $arguments, - $parameterReflection->getName(), - $newPosition - ); - } - - if ($newPosition === -1) { - return null; - } - - $position = $newPosition; - } - - if (! isset($arguments[$position])) { - return null; - } - - $particularArg = $arguments[$position] ?? null; - if (! $particularArg instanceof Arg) { - return null; - } - - $argValue = $this->valueResolver->getValue($particularArg->value); - if (is_scalar( - $replaceArgumentDefaultValue->getValueBefore() - ) && $argValue === $replaceArgumentDefaultValue->getValueBefore()) { - $normalizedValueAfter = $this->normalizeValue($replaceArgumentDefaultValue->getValueAfter()); - if ($particularArg->value instanceof ClassConstFetch - && $particularArg->value->class instanceof Name - && $normalizedValueAfter instanceof ClassConstFetch - && is_string($replaceArgumentDefaultValue->getValueAfter()) - && str_contains($replaceArgumentDefaultValue->getValueAfter(), '::')) { - [$targetClass, $targetConstant] = explode('::', $replaceArgumentDefaultValue->getValueAfter()); - $type = $this->nodeTypeResolver->getType($particularArg->value->class); - if ($type instanceof FullyQualifiedObjectType - && $type->getClassName() === $targetClass - && $particularArg->value->name instanceof Identifier - && $particularArg->value->name->toString() === $targetConstant) { - return null; - } - } - - // when the replacement value is a self::/static::/parent:: constant, it only - // resolves correctly if the class around the call actually has that constant; - // otherwise the produced code would reference a non-existing constant - if ($normalizedValueAfter instanceof ClassConstFetch - && $normalizedValueAfter->class instanceof Name - && $normalizedValueAfter->class->isSpecialClassName() - && $normalizedValueAfter->name instanceof Identifier) { - $classReflection = $this->reflectionResolver->resolveClassReflection($expr); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - if (! $classReflection->hasConstant($normalizedValueAfter->name->toString())) { - return null; - } - } - - $particularArg->value = $normalizedValueAfter; - return $expr; - } - - if (is_array($replaceArgumentDefaultValue->getValueBefore())) { - $newArgs = $this->processArrayReplacement($expr->getArgs(), $replaceArgumentDefaultValue); - if (is_array($newArgs)) { - $expr->args = $newArgs; - return $expr; - } - } - - return null; - } - - private function normalizeValueToArgument(mixed $value): Arg - { - return new Arg($this->normalizeValue($value)); - } - - private function normalizeValue(mixed $value): ClassConstFetch|Expr - { - // class constants → turn string to composite - if (is_string($value) && \str_contains($value, '::')) { - [$class, $constant] = explode('::', $value); - return $this->nodeFactory->createClassConstFetch($class, $constant); - } - - return BuilderHelpers::normalizeValue($value); - } - - /** - * @param array $args - * @return array|null - */ - private function processArrayReplacement( - array $args, - ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue - ): ?array { - $argumentValues = $this->resolveArgumentValuesToBeforeRecipe($args, $replaceArgumentDefaultValue); - if ($argumentValues !== $replaceArgumentDefaultValue->getValueBefore()) { - return null; - } - - if (is_string($replaceArgumentDefaultValue->getValueAfter())) { - $args[$replaceArgumentDefaultValue->getPosition()] = $this->normalizeValueToArgument( - $replaceArgumentDefaultValue->getValueAfter() - ); - - // clear following arguments - $argumentCountToClear = count($replaceArgumentDefaultValue->getValueBefore()); - for ($i = $replaceArgumentDefaultValue->getPosition() + 1; $i <= $replaceArgumentDefaultValue->getPosition() + $argumentCountToClear; ++$i) { - unset($args[$i]); - } - } - - return $args; - } - - /** - * @param Arg[] $argumentNodes - * @return mixed[] - */ - private function resolveArgumentValuesToBeforeRecipe( - array $argumentNodes, - ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue - ): array { - $argumentValues = []; - - $valueBefore = $replaceArgumentDefaultValue->getValueBefore(); - if (! is_array($valueBefore)) { - return []; - } - - $beforeArgumentCount = count($valueBefore); - - for ($i = 0; $i < $beforeArgumentCount; ++$i) { - if (! isset($argumentNodes[$replaceArgumentDefaultValue->getPosition() + $i])) { - continue; - } - - $nextArg = $argumentNodes[$replaceArgumentDefaultValue->getPosition() + $i]; - $argumentValues[] = $this->valueResolver->getValue($nextArg->value); - } - - return $argumentValues; - } -} diff --git a/rules/Arguments/Contract/ReplaceArgumentDefaultValueInterface.php b/rules/Arguments/Contract/ReplaceArgumentDefaultValueInterface.php deleted file mode 100644 index 994dac5277e..00000000000 --- a/rules/Arguments/Contract/ReplaceArgumentDefaultValueInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - $position diff --git a/rules/Arguments/ValueObject/ReplaceFuncCallArgumentDefaultValue.php b/rules/Arguments/ValueObject/ReplaceFuncCallArgumentDefaultValue.php index b5a7835457b..dcc0bbb92fd 100644 --- a/rules/Arguments/ValueObject/ReplaceFuncCallArgumentDefaultValue.php +++ b/rules/Arguments/ValueObject/ReplaceFuncCallArgumentDefaultValue.php @@ -4,12 +4,10 @@ namespace Rector\Arguments\ValueObject; -use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface; - /** * @api */ -final readonly class ReplaceFuncCallArgumentDefaultValue implements ReplaceArgumentDefaultValueInterface +final readonly class ReplaceFuncCallArgumentDefaultValue { public function __construct( private string $function,