Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apply parameter closure type extension in FunctionCallParametersCheck #3347

Open
wants to merge 8 commits into
base: 1.12.x
Choose a base branch
from
2 changes: 2 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,8 @@ services:
-
class: PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider
factory: PHPStan\DependencyInjection\Type\LazyParameterClosureTypeExtensionProvider
-
class: PHPStan\Type\ParameterClosureTypeHelper

-
class: PHPStan\File\FileHelper
Expand Down
38 changes: 4 additions & 34 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
use PHPStan\BetterReflection\SourceLocator\Located\LocatedSource;
use PHPStan\DependencyInjection\Reflection\ClassReflectionExtensionRegistryProvider;
use PHPStan\DependencyInjection\Type\DynamicThrowTypeExtensionProvider;
use PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider;
use PHPStan\DependencyInjection\Type\ParameterOutTypeExtensionProvider;
use PHPStan\File\FileHelper;
use PHPStan\File\FileReader;
Expand Down Expand Up @@ -171,6 +170,7 @@
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\ParameterClosureTypeHelper;
use PHPStan\Type\ResourceType;
use PHPStan\Type\StaticType;
use PHPStan\Type\StaticTypeFactory;
Expand Down Expand Up @@ -250,7 +250,7 @@ public function __construct(
private readonly TypeSpecifier $typeSpecifier,
private readonly DynamicThrowTypeExtensionProvider $dynamicThrowTypeExtensionProvider,
private readonly ReadWritePropertiesExtensionProvider $readWritePropertiesExtensionProvider,
private readonly ParameterClosureTypeExtensionProvider $parameterClosureTypeExtensionProvider,
private readonly ParameterClosureTypeHelper $parameterClosureTypeHelper,
private readonly ScopeFactory $scopeFactory,
private readonly bool $polluteScopeWithLoopInitialAssignments,
private readonly bool $polluteScopeWithAlwaysIterableForeach,
Expand Down Expand Up @@ -4621,7 +4621,7 @@ private function processArgs(
}

if ($parameter !== null) {
$overwritingParameterType = $this->getParameterTypeFromParameterClosureTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass);
$overwritingParameterType = $this->parameterClosureTypeHelper->getParameterTypeFromParameterClosureTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass);

if ($overwritingParameterType !== null) {
$parameterType = $overwritingParameterType;
Expand Down Expand Up @@ -4673,7 +4673,7 @@ private function processArgs(
}

if ($parameter !== null) {
$overwritingParameterType = $this->getParameterTypeFromParameterClosureTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass);
$overwritingParameterType = $this->parameterClosureTypeHelper->getParameterTypeFromParameterClosureTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass);

if ($overwritingParameterType !== null) {
$parameterType = $overwritingParameterType;
Expand Down Expand Up @@ -4820,36 +4820,6 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
return new ExpressionResult($scope, $hasYield, $throwPoints, $impurePoints);
}

/**
* @param MethodReflection|FunctionReflection|null $calleeReflection
*/
private function getParameterTypeFromParameterClosureTypeExtension(CallLike $callLike, $calleeReflection, ParameterReflection $parameter, MutatingScope $scope): ?Type
{
if ($callLike instanceof FuncCall && $calleeReflection instanceof FunctionReflection) {
foreach ($this->parameterClosureTypeExtensionProvider->getFunctionParameterClosureTypeExtensions() as $functionParameterClosureTypeExtension) {
if ($functionParameterClosureTypeExtension->isFunctionSupported($calleeReflection, $parameter)) {
return $functionParameterClosureTypeExtension->getTypeFromFunctionCall($calleeReflection, $callLike, $parameter, $scope);
}
}
} elseif ($calleeReflection instanceof MethodReflection) {
if ($callLike instanceof StaticCall) {
foreach ($this->parameterClosureTypeExtensionProvider->getStaticMethodParameterClosureTypeExtensions() as $staticMethodParameterClosureTypeExtension) {
if ($staticMethodParameterClosureTypeExtension->isStaticMethodSupported($calleeReflection, $parameter)) {
return $staticMethodParameterClosureTypeExtension->getTypeFromStaticMethodCall($calleeReflection, $callLike, $parameter, $scope);
}
}
} elseif ($callLike instanceof MethodCall) {
foreach ($this->parameterClosureTypeExtensionProvider->getMethodParameterClosureTypeExtensions() as $methodParameterClosureTypeExtension) {
if ($methodParameterClosureTypeExtension->isMethodSupported($calleeReflection, $parameter)) {
return $methodParameterClosureTypeExtension->getTypeFromMethodCall($calleeReflection, $callLike, $parameter, $scope);
}
}
}
}

return null;
}

/**
* @param MethodReflection|FunctionReflection|null $calleeReflection
*/
Expand Down
1 change: 1 addition & 0 deletions src/Rules/AttributesCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ public function check(
],
'attribute',
$attributeConstructor->acceptsNamedArguments(),
$attributeConstructor,
);

foreach ($parameterErrors as $error) {
Expand Down
1 change: 1 addition & 0 deletions src/Rules/Classes/InstantiationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ private function checkClassName(string $class, bool $isName, Node $node, Scope $
],
'new',
$constructorReflection->acceptsNamedArguments(),
$constructorReflection,
));
}

Expand Down
14 changes: 13 additions & 1 deletion src/Rules/FunctionCallParametersCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use PHPStan\Analyser\MutatingScope;
use PHPStan\Analyser\Scope;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Reflection\ParameterReflectionWithPhpDocs;
use PHPStan\Reflection\ParametersAcceptor;
Expand All @@ -20,6 +22,7 @@
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\NeverType;
use PHPStan\Type\ParameterClosureTypeHelper;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeTraverser;
Expand All @@ -42,6 +45,7 @@ public function __construct(
private PhpVersion $phpVersion,
private UnresolvableTypeHelper $unresolvableTypeHelper,
private PropertyReflectionFinder $propertyReflectionFinder,
private ParameterClosureTypeHelper $parameterClosureTypeHelper,
private bool $checkArgumentTypes,
private bool $checkArgumentsPassedByReference,
private bool $checkExtraArguments,
Expand All @@ -55,6 +59,7 @@ public function __construct(
* @param Node\Expr\FuncCall|Node\Expr\MethodCall|Node\Expr\StaticCall|Node\Expr\New_ $funcCall
* @param array{0: string, 1: string, 2: string, 3: string, 4: string, 5: string, 6: string, 7: string, 8: string, 9: string, 10: string, 11: string, 12: string, 13?: string, 14?: string} $messages
* @param 'attribute'|'callable'|'method'|'staticMethod'|'function'|'new' $nodeType
* @param MethodReflection|FunctionReflection|null $callReflection
* @return list<IdentifierRuleError>
*/
public function check(
Expand All @@ -65,6 +70,7 @@ public function check(
array $messages,
string $nodeType = 'function',
bool $acceptsNamedArguments = true,
$callReflection = null,
): array
{
$functionParametersMinCount = 0;
Expand Down Expand Up @@ -311,7 +317,13 @@ public function check(
}

if ($this->checkArgumentTypes) {
$parameterType = TypeUtils::resolveLateResolvableTypes($parameter->getType());
$parameterType = $this->parameterClosureTypeHelper->getParameterTypeFromParameterClosureTypeExtension(
$funcCall,
$callReflection,
$parameter,
$scope,
) ?? $parameter->getType();
$parameterType = TypeUtils::resolveLateResolvableTypes($parameterType);

if (
!$parameter->passedByReference()->createsNewVariable()
Expand Down
1 change: 1 addition & 0 deletions src/Rules/Functions/CallToFunctionParametersRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public function processNode(Node $node, Scope $scope): array
],
'function',
$function->acceptsNamedArguments(),
$function,
);
}

Expand Down
1 change: 1 addition & 0 deletions src/Rules/Methods/CallMethodsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public function processNode(Node $node, Scope $scope): array
],
'method',
$methodReflection->acceptsNamedArguments(),
$methodReflection,
));
}

Expand Down
1 change: 1 addition & 0 deletions src/Rules/Methods/CallStaticMethodsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public function processNode(Node $node, Scope $scope): array
],
'staticMethod',
$method->acceptsNamedArguments(),
$method,
));

return $errors;
Expand Down
4 changes: 2 additions & 2 deletions src/Testing/RuleTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use PHPStan\Collectors\Registry as CollectorRegistry;
use PHPStan\Dependency\DependencyResolver;
use PHPStan\DependencyInjection\Type\DynamicThrowTypeExtensionProvider;
use PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider;
use PHPStan\DependencyInjection\Type\ParameterOutTypeExtensionProvider;
use PHPStan\File\FileHelper;
use PHPStan\Php\PhpVersion;
Expand All @@ -30,6 +29,7 @@
use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Type\FileTypeMapper;
use PHPStan\Type\ParameterClosureTypeHelper;
use function array_map;
use function count;
use function implode;
Expand Down Expand Up @@ -95,7 +95,7 @@ private function getAnalyser(DirectRuleRegistry $ruleRegistry): Analyser
$typeSpecifier,
self::getContainer()->getByType(DynamicThrowTypeExtensionProvider::class),
$readWritePropertiesExtensions !== [] ? new DirectReadWritePropertiesExtensionProvider($readWritePropertiesExtensions) : self::getContainer()->getByType(ReadWritePropertiesExtensionProvider::class),
self::getContainer()->getByType(ParameterClosureTypeExtensionProvider::class),
self::getContainer()->getByType(ParameterClosureTypeHelper::class),
self::createScopeFactory($reflectionProvider, $typeSpecifier),
$this->shouldPolluteScopeWithLoopInitialAssignments(),
$this->shouldPolluteScopeWithAlwaysIterableForeach(),
Expand Down
4 changes: 2 additions & 2 deletions src/Testing/TypeInferenceTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\ScopeContext;
use PHPStan\DependencyInjection\Type\DynamicThrowTypeExtensionProvider;
use PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider;
use PHPStan\DependencyInjection\Type\ParameterOutTypeExtensionProvider;
use PHPStan\File\FileHelper;
use PHPStan\File\SystemAgnosticSimpleRelativePathHelper;
Expand All @@ -22,6 +21,7 @@
use PHPStan\TrinaryLogic;
use PHPStan\Type\ConstantScalarType;
use PHPStan\Type\FileTypeMapper;
use PHPStan\Type\ParameterClosureTypeHelper;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
use Symfony\Component\Finder\Finder;
Expand Down Expand Up @@ -75,7 +75,7 @@ public static function processFile(
$typeSpecifier,
self::getContainer()->getByType(DynamicThrowTypeExtensionProvider::class),
self::getContainer()->getByType(ReadWritePropertiesExtensionProvider::class),
self::getContainer()->getByType(ParameterClosureTypeExtensionProvider::class),
self::getContainer()->getByType(ParameterClosureTypeHelper::class),
self::createScopeFactory($reflectionProvider, $typeSpecifier),
self::getContainer()->getParameter('polluteScopeWithLoopInitialAssignments'),
self::getContainer()->getParameter('polluteScopeWithAlwaysIterableForeach'),
Expand Down
52 changes: 52 additions & 0 deletions src/Type/ParameterClosureTypeHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type;

use PhpParser\Node\Expr\CallLike;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParameterReflection;

final class ParameterClosureTypeHelper
{

public function __construct(private readonly ParameterClosureTypeExtensionProvider $parameterClosureTypeExtensionProvider)
{
}

/**
* @param MethodReflection|FunctionReflection|null $calleeReflection
*/
public function getParameterTypeFromParameterClosureTypeExtension(CallLike $callLike, $calleeReflection, ParameterReflection $parameter, Scope $scope): ?Type
{
if ($callLike instanceof FuncCall && $calleeReflection instanceof FunctionReflection) {
foreach ($this->parameterClosureTypeExtensionProvider->getFunctionParameterClosureTypeExtensions() as $functionParameterClosureTypeExtension) {
if ($functionParameterClosureTypeExtension->isFunctionSupported($calleeReflection, $parameter)) {
return $functionParameterClosureTypeExtension->getTypeFromFunctionCall($calleeReflection, $callLike, $parameter, $scope);
}
}
} elseif ($calleeReflection instanceof MethodReflection) {
if ($callLike instanceof StaticCall) {
foreach ($this->parameterClosureTypeExtensionProvider->getStaticMethodParameterClosureTypeExtensions() as $staticMethodParameterClosureTypeExtension) {
if ($staticMethodParameterClosureTypeExtension->isStaticMethodSupported($calleeReflection, $parameter)) {
return $staticMethodParameterClosureTypeExtension->getTypeFromStaticMethodCall($calleeReflection, $callLike, $parameter, $scope);
}
}
} elseif ($callLike instanceof MethodCall) {
foreach ($this->parameterClosureTypeExtensionProvider->getMethodParameterClosureTypeExtensions() as $methodParameterClosureTypeExtension) {
if ($methodParameterClosureTypeExtension->isMethodSupported($calleeReflection, $parameter)) {
return $methodParameterClosureTypeExtension->getTypeFromMethodCall($calleeReflection, $callLike, $parameter, $scope);
}
}
}
}

return null;
}

}
4 changes: 2 additions & 2 deletions src/Type/Php/PregReplaceCallbackClosureTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use PHPStan\Reflection\Native\NativeParameterReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\TrinaryLogic;
use PHPStan\Type\ClosureType;
use PHPStan\Type\CallableType;
use PHPStan\Type\FunctionParameterClosureTypeExtension;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
Expand Down Expand Up @@ -49,7 +49,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return null;
}

return new ClosureType(
return new CallableType(
[
new NativeParameterReflection($parameter->getName(), $parameter->isOptional(), $matchesType, $parameter->passedByReference(), $parameter->isVariadic(), $parameter->getDefaultValue()),
],
Expand Down
4 changes: 2 additions & 2 deletions tests/PHPStan/Analyser/AnalyserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use PHPStan\Dependency\DependencyResolver;
use PHPStan\Dependency\ExportedNodeResolver;
use PHPStan\DependencyInjection\Type\DynamicThrowTypeExtensionProvider;
use PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider;
use PHPStan\DependencyInjection\Type\ParameterOutTypeExtensionProvider;
use PHPStan\Node\Printer\ExprPrinter;
use PHPStan\Node\Printer\Printer;
Expand All @@ -26,6 +25,7 @@
use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider;
use PHPStan\Testing\PHPStanTestCase;
use PHPStan\Type\FileTypeMapper;
use PHPStan\Type\ParameterClosureTypeHelper;
use stdClass;
use function array_map;
use function array_merge;
Expand Down Expand Up @@ -731,7 +731,7 @@ private function createAnalyser(bool $enableIgnoreErrorsWithinPhpDocs): Analyser
$typeSpecifier,
self::getContainer()->getByType(DynamicThrowTypeExtensionProvider::class),
self::getContainer()->getByType(ReadWritePropertiesExtensionProvider::class),
self::getContainer()->getByType(ParameterClosureTypeExtensionProvider::class),
self::getContainer()->getByType(ParameterClosureTypeHelper::class),
self::createScopeFactory($reflectionProvider, $typeSpecifier),
false,
true,
Expand Down
15 changes: 14 additions & 1 deletion tests/PHPStan/Analyser/Bug9307CallMethodsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Testing\RuleTestCase;
use PHPStan\Type\ParameterClosureTypeHelper;
use const PHP_VERSION_ID;

/**
Expand All @@ -26,7 +27,19 @@ protected function getRule(): Rule
$ruleLevelHelper = new RuleLevelHelper($reflectionProvider, true, false, true, true, false, true, false);
return new CallMethodsRule(
new MethodCallCheck($reflectionProvider, $ruleLevelHelper, true, true),
new FunctionCallParametersCheck($ruleLevelHelper, new NullsafeCheck(), new PhpVersion(PHP_VERSION_ID), new UnresolvableTypeHelper(), new PropertyReflectionFinder(), true, true, true, true, true),
new FunctionCallParametersCheck(
$ruleLevelHelper,
new NullsafeCheck(),
new PhpVersion(PHP_VERSION_ID),
new UnresolvableTypeHelper(),
new PropertyReflectionFinder(),
self::getContainer()->getByType(ParameterClosureTypeHelper::class),
true,
true,
true,
true,
true,
),
);
}

Expand Down
2 changes: 2 additions & 0 deletions tests/PHPStan/Rules/Classes/ClassAttributesRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Testing\RuleTestCase;
use PHPStan\Type\ParameterClosureTypeHelper;
use const PHP_VERSION_ID;

/**
Expand All @@ -34,6 +35,7 @@ protected function getRule(): Rule
new PhpVersion(80000),
new UnresolvableTypeHelper(),
new PropertyReflectionFinder(),
self::getContainer()->getByType(ParameterClosureTypeHelper::class),
true,
true,
true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Testing\RuleTestCase;
use PHPStan\Type\ParameterClosureTypeHelper;

/**
* @extends RuleTestCase<ClassConstantAttributesRule>
Expand All @@ -33,6 +34,7 @@ protected function getRule(): Rule
new PhpVersion(80000),
new UnresolvableTypeHelper(),
new PropertyReflectionFinder(),
self::getContainer()->getByType(ParameterClosureTypeHelper::class),
true,
true,
true,
Expand Down
Loading
Loading