diff --git a/build/PHPStan/Build/NamedArgumentsRule.php b/build/PHPStan/Build/NamedArgumentsRule.php index b44a10ddea..4ea05a3878 100644 --- a/build/PHPStan/Build/NamedArgumentsRule.php +++ b/build/PHPStan/Build/NamedArgumentsRule.php @@ -116,6 +116,10 @@ private function processArgs(ExtendedParametersAcceptor $acceptor, Scope $scope, $errorBuilders = []; $parameters = $acceptor->getParameters(); + if (count($parameters) !== count($normalizedArgs)) { + return []; + } + $defaultValueWasPassed = []; foreach ($normalizedArgs as $i => $normalizedArg) { if ($normalizedArg->unpack) { diff --git a/src/Reflection/ParametersAcceptorSelector.php b/src/Reflection/ParametersAcceptorSelector.php index 52161da0e4..a8ef3d3f7d 100644 --- a/src/Reflection/ParametersAcceptorSelector.php +++ b/src/Reflection/ParametersAcceptorSelector.php @@ -104,27 +104,30 @@ public static function selectFromArgs( $callbackParameters[] = new DummyParameter('item', $scope->getIterableValueType($argType), false, PassedByReference::createNo(), false, null); } } - $parameters[0] = new NativeParameterReflection( - $parameters[0]->getName(), - $parameters[0]->isOptional(), - new UnionType([ - new CallableType($callbackParameters, new MixedType(), false), - new NullType(), - ]), - $parameters[0]->passedByReference(), - $parameters[0]->isVariadic(), - $parameters[0]->getDefaultValue(), - ); - $parametersAcceptors = [ - new FunctionVariant( - $acceptor->getTemplateTypeMap(), - $acceptor->getResolvedTemplateTypeMap(), - $parameters, - $acceptor->isVariadic(), - $acceptor->getReturnType(), - $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), - ), - ]; + + if (isset($parameters[0])) { + $parameters[0] = new NativeParameterReflection( + $parameters[0]->getName(), + $parameters[0]->isOptional(), + new UnionType([ + new CallableType($callbackParameters, new MixedType(), false), + new NullType(), + ]), + $parameters[0]->passedByReference(), + $parameters[0]->isVariadic(), + $parameters[0]->getDefaultValue(), + ); + $parametersAcceptors = [ + new FunctionVariant( + $acceptor->getTemplateTypeMap(), + $acceptor->getResolvedTemplateTypeMap(), + $parameters, + $acceptor->isVariadic(), + $acceptor->getReturnType(), + $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), + ), + ]; + } } if (count($args) >= 3 && (bool) $args[0]->getAttribute(CurlSetOptArgVisitor::ATTRIBUTE_NAME)) { @@ -146,10 +149,9 @@ public static function selectFromArgs( $valueTypes[] = $valueType; } - if (count($valueTypes) !== 0) { - $acceptor = $parametersAcceptors[0]; - $parameters = $acceptor->getParameters(); - + $acceptor = $parametersAcceptors[0]; + $parameters = $acceptor->getParameters(); + if (count($valueTypes) !== 0 && isset($parameters[2])) { $parameters[2] = new NativeParameterReflection( $parameters[2]->getName(), $parameters[2]->isOptional(), @@ -163,7 +165,7 @@ public static function selectFromArgs( new FunctionVariant( $acceptor->getTemplateTypeMap(), $acceptor->getResolvedTemplateTypeMap(), - array_values($parameters), + $parameters, $acceptor->isVariadic(), $acceptor->getReturnType(), $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), @@ -196,10 +198,9 @@ public static function selectFromArgs( ); } - if ($hasTypes) { - $acceptor = $parametersAcceptors[0]; - $parameters = $acceptor->getParameters(); - + $acceptor = $parametersAcceptors[0]; + $parameters = $acceptor->getParameters(); + if ($hasTypes && isset($parameters[1])) { $parameters[1] = new NativeParameterReflection( $parameters[1]->getName(), $parameters[1]->isOptional(), @@ -213,7 +214,7 @@ public static function selectFromArgs( new FunctionVariant( $acceptor->getTemplateTypeMap(), $acceptor->getResolvedTemplateTypeMap(), - array_values($parameters), + $parameters, $acceptor->isVariadic(), $acceptor->getReturnType(), $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), @@ -241,44 +242,49 @@ public static function selectFromArgs( $acceptor = $parametersAcceptors[0]; $parameters = $acceptor->getParameters(); - $parameters[1] = new NativeParameterReflection( - $parameters[1]->getName(), - $parameters[1]->isOptional(), - new UnionType([ - new CallableType( - $arrayFilterParameters ?? [ - new DummyParameter('item', $scope->getIterableValueType($scope->getType($args[0]->value)), false, PassedByReference::createNo(), false, null), - ], - new BooleanType(), - false, + if (isset($parameters[1])) { + $parameters[1] = new NativeParameterReflection( + $parameters[1]->getName(), + $parameters[1]->isOptional(), + new UnionType([ + new CallableType( + $arrayFilterParameters ?? [ + new DummyParameter('item', $scope->getIterableValueType($scope->getType($args[0]->value)), false, PassedByReference::createNo(), false, null), + ], + new BooleanType(), + false, + ), + new NullType(), + ]), + $parameters[1]->passedByReference(), + $parameters[1]->isVariadic(), + $parameters[1]->getDefaultValue(), + ); + $parametersAcceptors = [ + new FunctionVariant( + $acceptor->getTemplateTypeMap(), + $acceptor->getResolvedTemplateTypeMap(), + $parameters, + $acceptor->isVariadic(), + $acceptor->getReturnType(), + $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), ), - new NullType(), - ]), - $parameters[1]->passedByReference(), - $parameters[1]->isVariadic(), - $parameters[1]->getDefaultValue(), - ); - $parametersAcceptors = [ - new FunctionVariant( - $acceptor->getTemplateTypeMap(), - $acceptor->getResolvedTemplateTypeMap(), - array_values($parameters), - $acceptor->isVariadic(), - $acceptor->getReturnType(), - $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), - ), - ]; + ]; + } } if (count($args) <= 2 && (bool) $args[0]->getAttribute(ImplodeArgVisitor::ATTRIBUTE_NAME)) { $acceptor = $namedArgumentsVariants[0] ?? $parametersAcceptors[0]; $parameters = $acceptor->getParameters(); - if (isset($args[1]) || ($args[0]->name !== null && $args[0]->name->name === 'array')) { + if ( + (isset($args[1]) || ($args[0]->name !== null && $args[0]->name->name === 'array')) + && isset($parameters[0]) && isset($parameters[1]) + ) { $parameters = [ new NativeParameterReflection($parameters[0]->getName(), false, new StringType(), PassedByReference::createNo(), false, null), new NativeParameterReflection($parameters[1]->getName(), false, new ArrayType(new MixedType(), new MixedType()), PassedByReference::createNo(), false, null), ]; - } else { + } elseif (isset($parameters[0])) { $parameters = [ new NativeParameterReflection($parameters[0]->getName(), false, new ArrayType(new MixedType(), new MixedType()), PassedByReference::createNo(), false, null), ]; @@ -307,55 +313,59 @@ public static function selectFromArgs( $acceptor = $parametersAcceptors[0]; $parameters = $acceptor->getParameters(); - $parameters[1] = new NativeParameterReflection( - $parameters[1]->getName(), - $parameters[1]->isOptional(), - new CallableType($arrayWalkParameters, new MixedType(), false), - $parameters[1]->passedByReference(), - $parameters[1]->isVariadic(), - $parameters[1]->getDefaultValue(), - ); - $parametersAcceptors = [ - new FunctionVariant( - $acceptor->getTemplateTypeMap(), - $acceptor->getResolvedTemplateTypeMap(), - array_values($parameters), - $acceptor->isVariadic(), - $acceptor->getReturnType(), - $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), - ), - ]; + if (isset($parameters[1])) { + $parameters[1] = new NativeParameterReflection( + $parameters[1]->getName(), + $parameters[1]->isOptional(), + new CallableType($arrayWalkParameters, new MixedType(), false), + $parameters[1]->passedByReference(), + $parameters[1]->isVariadic(), + $parameters[1]->getDefaultValue(), + ); + $parametersAcceptors = [ + new FunctionVariant( + $acceptor->getTemplateTypeMap(), + $acceptor->getResolvedTemplateTypeMap(), + $parameters, + $acceptor->isVariadic(), + $acceptor->getReturnType(), + $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), + ), + ]; + } } if ((bool) $args[0]->getAttribute(ArrayFindArgVisitor::ATTRIBUTE_NAME)) { $acceptor = $parametersAcceptors[0]; $parameters = $acceptor->getParameters(); - $argType = $scope->getType($args[0]->value); - $parameters[1] = new NativeParameterReflection( - $parameters[1]->getName(), - $parameters[1]->isOptional(), - new CallableType( - [ - new DummyParameter('value', $scope->getIterableValueType($argType), false, PassedByReference::createNo(), false, null), - new DummyParameter('key', $scope->getIterableKeyType($argType), false, PassedByReference::createNo(), false, null), - ], - new BooleanType(), - false, - ), - $parameters[1]->passedByReference(), - $parameters[1]->isVariadic(), - $parameters[1]->getDefaultValue(), - ); - $parametersAcceptors = [ - new FunctionVariant( - $acceptor->getTemplateTypeMap(), - $acceptor->getResolvedTemplateTypeMap(), - array_values($parameters), - $acceptor->isVariadic(), - $acceptor->getReturnType(), - $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), - ), - ]; + if (isset($parameters[1])) { + $argType = $scope->getType($args[0]->value); + $parameters[1] = new NativeParameterReflection( + $parameters[1]->getName(), + $parameters[1]->isOptional(), + new CallableType( + [ + new DummyParameter('value', $scope->getIterableValueType($argType), false, PassedByReference::createNo(), false, null), + new DummyParameter('key', $scope->getIterableKeyType($argType), false, PassedByReference::createNo(), false, null), + ], + new BooleanType(), + false, + ), + $parameters[1]->passedByReference(), + $parameters[1]->isVariadic(), + $parameters[1]->getDefaultValue(), + ); + $parametersAcceptors = [ + new FunctionVariant( + $acceptor->getTemplateTypeMap(), + $acceptor->getResolvedTemplateTypeMap(), + $parameters, + $acceptor->isVariadic(), + $acceptor->getReturnType(), + $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), + ), + ]; + } } $closureBindToVar = $args[0]->getAttribute(ClosureBindToVarVisitor::ATTRIBUTE_NAME); @@ -378,24 +388,26 @@ public static function selectFromArgs( if ($scope->hasExpressionType(new ParameterVariableOriginalValueExpr($closureBindToVar->name))->yes()) { $acceptor = $parametersAcceptors[0]; $parameters = $acceptor->getParameters(); - $parameters[0] = new NativeParameterReflection( - $parameters[0]->getName(), - $parameters[0]->isOptional(), - $closureThisParameters[$closureBindToVar->name], - $parameters[0]->passedByReference(), - $parameters[0]->isVariadic(), - $parameters[0]->getDefaultValue(), - ); - $parametersAcceptors = [ - new FunctionVariant( - $acceptor->getTemplateTypeMap(), - $acceptor->getResolvedTemplateTypeMap(), - $parameters, - $acceptor->isVariadic(), - $acceptor->getReturnType(), - $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), - ), - ]; + if (isset($parameters[0])) { + $parameters[0] = new NativeParameterReflection( + $parameters[0]->getName(), + $parameters[0]->isOptional(), + $closureThisParameters[$closureBindToVar->name], + $parameters[0]->passedByReference(), + $parameters[0]->isVariadic(), + $parameters[0]->getDefaultValue(), + ); + $parametersAcceptors = [ + new FunctionVariant( + $acceptor->getTemplateTypeMap(), + $acceptor->getResolvedTemplateTypeMap(), + $parameters, + $acceptor->isVariadic(), + $acceptor->getReturnType(), + $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), + ), + ]; + } } } } @@ -421,24 +433,27 @@ public static function selectFromArgs( if ($scope->hasExpressionType(new ParameterVariableOriginalValueExpr($closureVarName))->yes()) { $acceptor = $parametersAcceptors[0]; $parameters = $acceptor->getParameters(); - $parameters[1] = new NativeParameterReflection( - $parameters[1]->getName(), - $parameters[1]->isOptional(), - $closureThisParameters[$closureVarName], - $parameters[1]->passedByReference(), - $parameters[1]->isVariadic(), - $parameters[1]->getDefaultValue(), - ); - $parametersAcceptors = [ - new FunctionVariant( - $acceptor->getTemplateTypeMap(), - $acceptor->getResolvedTemplateTypeMap(), - array_values($parameters), - $acceptor->isVariadic(), - $acceptor->getReturnType(), - $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), - ), - ]; + + if (isset($parameters[1])) { + $parameters[1] = new NativeParameterReflection( + $parameters[1]->getName(), + $parameters[1]->isOptional(), + $closureThisParameters[$closureVarName], + $parameters[1]->passedByReference(), + $parameters[1]->isVariadic(), + $parameters[1]->getDefaultValue(), + ); + $parametersAcceptors = [ + new FunctionVariant( + $acceptor->getTemplateTypeMap(), + $acceptor->getResolvedTemplateTypeMap(), + $parameters, + $acceptor->isVariadic(), + $acceptor->getReturnType(), + $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), + ), + ]; + } } } } diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index edae4aeeab..8d32d02ba3 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -1556,6 +1556,19 @@ public function testBug13310(): void $this->assertNoErrors($errors); } + public function testBug13714(): void + { + $errors = $this->runAnalyse(__DIR__ . '/data/bug-13714.php'); + $this->assertCount(7, $errors); + $this->assertSame('Function Bug13714\curl_setopt invoked with 3 parameters, 0 required.', $errors[0]->getMessage()); + $this->assertSame('Function Bug13714\curl_setopt_array invoked with 2 parameters, 0 required.', $errors[1]->getMessage()); + $this->assertSame('Function Bug13714\implode invoked with 2 parameters, 0 required.', $errors[2]->getMessage()); + $this->assertSame('Function Bug13714\array_map invoked with 2 parameters, 0 required.', $errors[3]->getMessage()); + $this->assertSame('Function Bug13714\array_filter invoked with 2 parameters, 0 required.', $errors[4]->getMessage()); + $this->assertSame('Function Bug13714\array_walk invoked with 2 parameters, 0 required.', $errors[5]->getMessage()); + $this->assertSame('Function Bug13714\array_find invoked with 2 parameters, 0 required.', $errors[6]->getMessage()); + } + /** * @param string[]|null $allAnalysedFiles * @return list diff --git a/tests/PHPStan/Analyser/data/bug-13714.php b/tests/PHPStan/Analyser/data/bug-13714.php new file mode 100644 index 0000000000..e74abe7a92 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-13714.php @@ -0,0 +1,48 @@ + "lemon", "a" => "orange", "b" => "banana", "c" => "apple"); +function test_print(string $item2, string $key):void +{ + echo "$key. $item2\n"; +} +array_walk($fruits, 'test_print'); + +$array = [ + 'a' => 'dog', + 'b' => 'cat', + 'c' => 'cow', + 'd' => 'duck', + 'e' => 'goose', + 'f' => 'elephant' +]; +function array_find():void {} +array_find($array, function (string $value) { + return strlen($value) > 4; +});