Skip to content

Commit a599eaa

Browse files
committed
Fix variadic parameters by not making them always ArrayType
1 parent 4ba83e8 commit a599eaa

24 files changed

+271
-118
lines changed

src/Analyser/MutatingScope.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2289,7 +2289,7 @@ private function getRealParameterTypes(Node\FunctionLike $functionLike): array
22892289
$realParameterTypes[$parameter->var->name] = $this->getFunctionType(
22902290
$parameter->type,
22912291
$this->isParameterValueNullable($parameter),
2292-
$parameter->variadic
2292+
false
22932293
);
22942294
}
22952295

@@ -2368,7 +2368,11 @@ private function enterFunctionLike(
23682368
$variableTypes = $this->getVariableTypes();
23692369
$nativeExpressionTypes = [];
23702370
foreach (ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getParameters() as $parameter) {
2371-
$variableTypes[$parameter->getName()] = VariableTypeHolder::createYes($parameter->getType());
2371+
$parameterType = $parameter->getType();
2372+
if ($parameter->isVariadic()) {
2373+
$parameterType = new ArrayType(new IntegerType(), $parameterType);
2374+
}
2375+
$variableTypes[$parameter->getName()] = VariableTypeHolder::createYes($parameterType);
23722376
$nativeExpressionTypes[sprintf('$%s', $parameter->getName())] = $parameter->getNativeType();
23732377
}
23742378

src/PhpDoc/PhpDocNodeResolver.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@
2222
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
2323
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
2424
use PHPStan\Reflection\PassedByReference;
25-
use PHPStan\Type\ArrayType;
2625
use PHPStan\Type\ErrorType;
2726
use PHPStan\Type\Generic\TemplateTypeVariance;
28-
use PHPStan\Type\IntegerType;
2927
use PHPStan\Type\MixedType;
3028
use PHPStan\Type\NeverType;
3129
use PHPStan\Type\Type;
@@ -290,15 +288,6 @@ public function resolveParamTags(PhpDocNode $phpDocNode, NameScope $nameScope):
290288
continue;
291289
}
292290

293-
if ($tagValue->isVariadic) {
294-
if (!$parameterType instanceof ArrayType) {
295-
$parameterType = new ArrayType(new IntegerType(), $parameterType);
296-
297-
} elseif ($parameterType->getKeyType() instanceof MixedType) {
298-
$parameterType = new ArrayType(new IntegerType(), $parameterType->getItemType());
299-
}
300-
}
301-
302291
$resolved[$parameterName] = new ParamTag(
303292
$parameterType,
304293
$tagValue->isVariadic

src/Reflection/GenericParametersAcceptorResolver.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
namespace PHPStan\Reflection;
44

55
use PHPStan\Reflection\Generic\ResolvedFunctionVariant;
6-
use PHPStan\Type\ArrayType;
76
use PHPStan\Type\ErrorType;
87
use PHPStan\Type\Generic\TemplateTypeMap;
9-
use PHPStan\Type\MixedType;
108
use PHPStan\Type\Type;
11-
use PHPStan\Type\TypeCombinator;
129

1310
class GenericParametersAcceptorResolver
1411
{
@@ -24,11 +21,7 @@ public static function resolve(array $argTypes, ParametersAcceptor $parametersAc
2421

2522
foreach ($parametersAcceptor->getParameters() as $i => $param) {
2623
if (isset($argTypes[$i])) {
27-
if ($param->isVariadic()) {
28-
$argType = new ArrayType(new MixedType(), TypeCombinator::union(...array_slice($argTypes, $i)));
29-
} else {
30-
$argType = $argTypes[$i];
31-
}
24+
$argType = $argTypes[$i];
3225
} elseif ($param->getDefaultValue() !== null) {
3326
$argType = $param->getDefaultValue();
3427
} else {

src/Reflection/Native/NativeParameterReflection.php

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
use PHPStan\Reflection\ParameterReflection;
66
use PHPStan\Reflection\PassedByReference;
7-
use PHPStan\Type\ArrayType;
8-
use PHPStan\Type\IntegerType;
97
use PHPStan\Type\Type;
108

119
class NativeParameterReflection implements ParameterReflection
@@ -23,16 +21,13 @@ class NativeParameterReflection implements ParameterReflection
2321

2422
private ?\PHPStan\Type\Type $defaultValue;
2523

26-
private bool $variadicParameterAlreadyExpanded;
27-
2824
public function __construct(
2925
string $name,
3026
bool $optional,
3127
Type $type,
3228
PassedByReference $passedByReference,
3329
bool $variadic,
34-
?Type $defaultValue,
35-
bool $variadicParameterAlreadyExpanded = false
30+
?Type $defaultValue
3631
)
3732
{
3833
$this->name = $name;
@@ -41,7 +36,6 @@ public function __construct(
4136
$this->passedByReference = $passedByReference;
4237
$this->variadic = $variadic;
4338
$this->defaultValue = $defaultValue;
44-
$this->variadicParameterAlreadyExpanded = $variadicParameterAlreadyExpanded;
4539
}
4640

4741
public function getName(): string
@@ -56,12 +50,7 @@ public function isOptional(): bool
5650

5751
public function getType(): Type
5852
{
59-
$type = $this->type;
60-
if ($this->variadic && !$this->variadicParameterAlreadyExpanded) {
61-
$type = new ArrayType(new IntegerType(), $type);
62-
}
63-
64-
return $type;
53+
return $this->type;
6554
}
6655

6756
public function passedByReference(): PassedByReference

src/Reflection/Native/NativeParameterWithPhpDocsReflection.php

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
use PHPStan\Reflection\ParameterReflectionWithPhpDocs;
66
use PHPStan\Reflection\PassedByReference;
7-
use PHPStan\Type\ArrayType;
8-
use PHPStan\Type\IntegerType;
97
use PHPStan\Type\Type;
108

119
class NativeParameterWithPhpDocsReflection implements ParameterReflectionWithPhpDocs
@@ -27,8 +25,6 @@ class NativeParameterWithPhpDocsReflection implements ParameterReflectionWithPhp
2725

2826
private ?\PHPStan\Type\Type $defaultValue;
2927

30-
private bool $variadicParameterAlreadyExpanded;
31-
3228
public function __construct(
3329
string $name,
3430
bool $optional,
@@ -37,8 +33,7 @@ public function __construct(
3733
Type $nativeType,
3834
PassedByReference $passedByReference,
3935
bool $variadic,
40-
?Type $defaultValue,
41-
bool $variadicParameterAlreadyExpanded = false
36+
?Type $defaultValue
4237
)
4338
{
4439
$this->name = $name;
@@ -49,7 +44,6 @@ public function __construct(
4944
$this->passedByReference = $passedByReference;
5045
$this->variadic = $variadic;
5146
$this->defaultValue = $defaultValue;
52-
$this->variadicParameterAlreadyExpanded = $variadicParameterAlreadyExpanded;
5347
}
5448

5549
public function getName(): string
@@ -64,12 +58,7 @@ public function isOptional(): bool
6458

6559
public function getType(): Type
6660
{
67-
$type = $this->type;
68-
if ($this->variadic && !$this->variadicParameterAlreadyExpanded) {
69-
$type = new ArrayType(new IntegerType(), $type);
70-
}
71-
72-
return $type;
61+
return $this->type;
7362
}
7463

7564
public function getPhpDocType(): Type

src/Reflection/Php/PhpClassReflectionExtension.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,7 @@ private function createNativeMethodVariant(
580580
$nativeReturnType = TypehintHelper::decideTypeFromReflection(
581581
$reflectionMethod->getReturnType(),
582582
null,
583-
null,
584-
false
583+
null
585584
);
586585
}
587586
foreach ($methodSignature->getParameters() as $i => $parameterSignature) {
@@ -606,8 +605,7 @@ private function createNativeMethodVariant(
606605
$nativeParameterType,
607606
$parameterSignature->passedByReference(),
608607
$stubPhpDocParameterVariadicity[$parameterSignature->getName()] ?? $parameterSignature->isVariadic(),
609-
null,
610-
isset($stubPhpDocParameterTypes[$parameterSignature->getName()])
608+
null
611609
);
612610
}
613611

src/Rules/FunctionCallParametersCheck.php

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44

55
use PHPStan\Analyser\Scope;
66
use PHPStan\Reflection\ParametersAcceptor;
7-
use PHPStan\Type\ArrayType;
87
use PHPStan\Type\ErrorType;
9-
use PHPStan\Type\IterableType;
10-
use PHPStan\Type\MixedType;
118
use PHPStan\Type\NeverType;
129
use PHPStan\Type\Type;
1310
use PHPStan\Type\VerbosityLevel;
@@ -149,44 +146,20 @@ static function (Type $type): bool {
149146
}
150147

151148
$parameter = $parameters[count($parameters) - 1];
152-
$parameterType = $parameter->getType();
153-
if (!($parameterType instanceof ArrayType)) {
154-
break;
155-
}
156-
157-
if (!$argument->unpack) {
158-
$parameterType = $parameterType->getItemType();
159-
}
160149
} else {
161150
$parameter = $parameters[$i];
162-
$parameterType = $parameter->getType();
163-
if ($parameter->isVariadic()) {
164-
if ($parameterType instanceof ArrayType && !$argument->unpack) {
165-
$parameterType = $parameterType->getItemType();
166-
}
167-
} elseif ($argument->unpack) {
168-
continue;
169-
}
170151
}
171152

153+
$parameterType = $parameter->getType();
154+
172155
$argumentValueType = $scope->getType($argument->value);
173-
$secondAccepts = null;
174-
if ($parameterType->isIterable()->yes() && $parameter->isVariadic()) {
175-
$secondAccepts = $this->ruleLevelHelper->accepts(
176-
new IterableType(
177-
new MixedType(),
178-
$parameterType->getIterableValueType()
179-
),
180-
$argumentValueType,
181-
$scope->isDeclareStrictTypes()
182-
);
156+
if ($argument->unpack) {
157+
$argumentValueType = $argumentValueType->getIterableValueType();
183158
}
184-
185159
if (
186160
$this->checkArgumentTypes
187161
&& !$parameter->passedByReference()->createsNewVariable()
188162
&& !$this->ruleLevelHelper->accepts($parameterType, $argumentValueType, $scope->isDeclareStrictTypes())
189-
&& ($secondAccepts === null || !$secondAccepts)
190163
) {
191164
$verbosityLevel = VerbosityLevel::getRecommendedLevelByType($parameterType);
192165
$errors[] = RuleErrorBuilder::message(sprintf(

src/Rules/FunctionDefinitionCheck.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public function checkAnonymousFunction(
9191
if (!$param->var instanceof Variable || !is_string($param->var->name)) {
9292
throw new \PHPStan\ShouldNotHappenException();
9393
}
94-
$type = $scope->getFunctionType($param->type, false, $param->variadic);
94+
$type = $scope->getFunctionType($param->type, false, false);
9595
if ($type instanceof VoidType) {
9696
$errors[] = RuleErrorBuilder::message(sprintf($parameterMessage, $param->var->name, 'void'))->line($param->type->getLine())->nonIgnorable()->build();
9797
}

src/Rules/PhpDoc/IncompatiblePhpDocTypeRule.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ public function processNode(Node $node, Scope $scope): array
8484

8585
} else {
8686
$nativeParamType = $nativeParameterTypes[$parameterName];
87+
if (
88+
$phpDocParamTag->isVariadic()
89+
&& $phpDocParamType instanceof ArrayType
90+
&& !$nativeParamType instanceof ArrayType
91+
) {
92+
$phpDocParamType = $phpDocParamType->getItemType();
93+
}
8794
$isParamSuperType = $nativeParamType->isSuperTypeOf(TemplateTypeHelper::resolveToBounds($phpDocParamType));
8895

8996
$errors = array_merge($errors, $this->genericObjectTypeCheck->check(
@@ -106,14 +113,6 @@ public function processNode(Node $node, Scope $scope): array
106113
)
107114
));
108115

109-
if (
110-
$phpDocParamTag->isVariadic()
111-
&& $nativeParamType instanceof ArrayType
112-
&& $nativeParamType->getItemType() instanceof ArrayType
113-
) {
114-
continue;
115-
}
116-
117116
if ($isParamSuperType->no()) {
118117
$errors[] = RuleErrorBuilder::message(sprintf(
119118
'PHPDoc tag @param for parameter $%s with type %s is incompatible with native type %s.',
@@ -187,7 +186,7 @@ private function getNativeParameterTypes(\PhpParser\Node\FunctionLike $node, Sco
187186
$nativeParameterTypes[$parameter->var->name] = $scope->getFunctionType(
188187
$parameter->type,
189188
$isNullable,
190-
$parameter->variadic
189+
false
191190
);
192191
}
193192

src/Type/CallableType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ function () use ($level): string {
130130
'callable(%s): %s',
131131
implode(', ', array_map(
132132
static function (NativeParameterReflection $param) use ($level): string {
133-
return $param->getType()->describe($level);
133+
return sprintf('%s%s', $param->isVariadic() ? '...' : '', $param->getType()->describe($level));
134134
},
135135
$this->getParameters()
136136
)),

0 commit comments

Comments
 (0)