Skip to content

Commit 7e50d0d

Browse files
committed
fix: apply dynamic parameter type extensions in FunctionCallParametersCheck
1 parent c24f544 commit 7e50d0d

30 files changed

Lines changed: 105 additions & 15 deletions

e2e/parameter-type-extension/phpstan-baseline.neon

Lines changed: 0 additions & 7 deletions
This file was deleted.

e2e/parameter-type-extension/phpstan.neon.dist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
includes:
2-
- phpstan-baseline.neon
31
parameters:
42
level: 9
53
paths:

src/Rules/AttributesCheck.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ public function check(
139139
),
140140
$scope,
141141
$attributeConstructor->getDeclaringClass()->isBuiltin(),
142+
null,
142143
new New_($attribute->name, $attribute->args, $nodeAttributes),
143144
'attribute',
144145
$attributeConstructor->acceptsNamedArguments(),

src/Rules/Classes/InstantiationRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ private function checkClassName(string $class, bool $isName, Node $node, Scope $
240240
),
241241
$scope,
242242
$constructorReflection->getDeclaringClass()->isBuiltin(),
243+
null,
243244
$node,
244245
'new',
245246
$constructorReflection->acceptsNamedArguments(),

src/Rules/FunctionCallParametersCheck.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
use PHPStan\Analyser\Scope;
99
use PHPStan\DependencyInjection\AutowiredParameter;
1010
use PHPStan\DependencyInjection\AutowiredService;
11+
use PHPStan\DependencyInjection\Type\DynamicParameterTypeExtensionProvider;
1112
use PHPStan\Reflection\ExtendedParameterReflection;
13+
use PHPStan\Reflection\FunctionReflection;
14+
use PHPStan\Reflection\MethodReflection;
1215
use PHPStan\Reflection\ParameterReflection;
1316
use PHPStan\Reflection\ParametersAcceptor;
1417
use PHPStan\Reflection\ReflectionProvider;
@@ -49,6 +52,7 @@ public function __construct(
4952
private UnresolvableTypeHelper $unresolvableTypeHelper,
5053
private PropertyReflectionFinder $propertyReflectionFinder,
5154
private ReflectionProvider $reflectionProvider,
55+
private DynamicParameterTypeExtensionProvider $dynamicParameterTypeExtensionProvider,
5256
#[AutowiredParameter(ref: '%checkFunctionArgumentTypes%')]
5357
private bool $checkArgumentTypes,
5458
#[AutowiredParameter]
@@ -69,6 +73,7 @@ public function check(
6973
ParametersAcceptor $parametersAcceptor,
7074
Scope $scope,
7175
bool $isBuiltin,
76+
MethodReflection|FunctionReflection|null $calleeReflection,
7277
Node\Expr\FuncCall|Node\Expr\MethodCall|Node\Expr\StaticCall|Node\Expr\New_ $funcCall,
7378
string $nodeType,
7479
TrinaryLogic $acceptsNamedArguments,
@@ -359,6 +364,13 @@ public function check(
359364
if ($this->checkArgumentTypes) {
360365
$parameterType = TypeUtils::resolveLateResolvableTypes($parameter->getType());
361366

367+
if (! $funcCall instanceof Node\Expr\New_) {
368+
$overriddenType = $this->getParameterTypeFromDynamicExtension($funcCall, $calleeReflection, $parameter, $scope);
369+
if ($overriddenType !== null) {
370+
$parameterType = $overriddenType;
371+
}
372+
}
373+
362374
if (
363375
!$parameter->passedByReference()->createsNewVariable()
364376
|| (!$isBuiltin && !$argumentValueType instanceof ErrorType)
@@ -744,4 +756,50 @@ private function callReturnsByReference(Expr $expr, Scope $scope): bool
744756
return false;
745757
}
746758

759+
private function getParameterTypeFromDynamicExtension(
760+
Node\Expr\FuncCall|Node\Expr\MethodCall|Node\Expr\StaticCall $funcCall,
761+
MethodReflection|FunctionReflection|null $calleeReflection,
762+
ParameterReflection $parameter,
763+
Scope $scope,
764+
): ?Type
765+
{
766+
if ($calleeReflection === null) {
767+
return null;
768+
}
769+
770+
if ($funcCall instanceof Node\Expr\FuncCall && $calleeReflection instanceof FunctionReflection) {
771+
foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicFunctionParameterTypeExtensions() as $extension) {
772+
if (!$extension->isFunctionSupported($calleeReflection, $parameter)) {
773+
continue;
774+
}
775+
$type = $extension->getTypeFromFunctionCall($calleeReflection, $funcCall, $parameter, $scope);
776+
if ($type !== null) {
777+
return $type;
778+
}
779+
}
780+
} elseif ($funcCall instanceof Node\Expr\StaticCall && $calleeReflection instanceof MethodReflection) {
781+
foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicStaticMethodParameterTypeExtensions() as $extension) {
782+
if (!$extension->isStaticMethodSupported($calleeReflection, $parameter)) {
783+
continue;
784+
}
785+
$type = $extension->getTypeFromStaticMethodCall($calleeReflection, $funcCall, $parameter, $scope);
786+
if ($type !== null) {
787+
return $type;
788+
}
789+
}
790+
} elseif ($funcCall instanceof Node\Expr\MethodCall && $calleeReflection instanceof MethodReflection) {
791+
foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicMethodParameterTypeExtensions() as $extension) {
792+
if (!$extension->isMethodSupported($calleeReflection, $parameter)) {
793+
continue;
794+
}
795+
$type = $extension->getTypeFromMethodCall($calleeReflection, $funcCall, $parameter, $scope);
796+
if ($type !== null) {
797+
return $type;
798+
}
799+
}
800+
}
801+
802+
return null;
803+
}
804+
747805
}

src/Rules/Functions/CallCallablesRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ public function processNode(
121121
$parametersAcceptor,
122122
$scope,
123123
false,
124+
null,
124125
$node,
125126
'callable',
126127
$acceptsNamedArguments,

src/Rules/Functions/CallToFunctionParametersRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public function processNode(Node $node, Scope $scope): array
5050
),
5151
$scope,
5252
$function->isBuiltin(),
53+
$function,
5354
$node,
5455
'function',
5556
$function->acceptsNamedArguments(),

src/Rules/Functions/CallUserFuncRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public function processNode(Node $node, Scope $scope): array
6666
$parametersAcceptor,
6767
$scope,
6868
false,
69+
null,
6970
$funcCall,
7071
'function',
7172
$acceptsNamedArguments,

src/Rules/Methods/CallMethodsRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ private function processSingleMethodCall(Scope $scope, MethodCall $node, string
8181
),
8282
$scope,
8383
$declaringClass->isBuiltin(),
84+
$methodReflection,
8485
$node,
8586
'method',
8687
$methodReflection->acceptsNamedArguments(),

src/Rules/Methods/CallStaticMethodsRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ private function processSingleMethodCall(Scope $scope, StaticCall $node, string
9090
),
9191
$scope,
9292
$method->getDeclaringClass()->isBuiltin(),
93+
$method,
9394
$node,
9495
'staticMethod',
9596
$method->acceptsNamedArguments(),

0 commit comments

Comments
 (0)