2
2
3
3
namespace PHPStan \Rules ;
4
4
5
+ use PhpParser \Node \Expr ;
5
6
use PHPStan \Analyser \Scope ;
6
7
use PHPStan \Reflection \ParametersAcceptor ;
7
8
use PHPStan \Type \ErrorType ;
8
9
use PHPStan \Type \NeverType ;
9
10
use PHPStan \Type \Type ;
11
+ use PHPStan \Type \TypeCombinator ;
12
+ use PHPStan \Type \TypeUtils ;
10
13
use PHPStan \Type \VerbosityLevel ;
11
14
use PHPStan \Type \VoidType ;
12
15
@@ -66,10 +69,57 @@ public function check(
66
69
$ functionParametersMaxCount = -1 ;
67
70
}
68
71
69
- $ errors = [];
70
- $ invokedParametersCount = count ($ funcCall ->args );
71
- foreach ($ funcCall ->args as $ arg ) {
72
+ /** @var array<int, array{Expr, Type, bool}> $arguments */
73
+ $ arguments = [];
74
+ /** @var array<int, \PhpParser\Node\Arg> $args */
75
+ $ args = $ funcCall ->args ;
76
+ foreach ($ args as $ i => $ arg ) {
77
+ $ type = $ scope ->getType ($ arg ->value );
72
78
if ($ arg ->unpack ) {
79
+ $ arrays = TypeUtils::getConstantArrays ($ type );
80
+ if (count ($ arrays ) > 0 ) {
81
+ $ minKeys = null ;
82
+ foreach ($ arrays as $ array ) {
83
+ $ keysCount = count ($ array ->getKeyTypes ());
84
+ if ($ minKeys !== null && $ keysCount >= $ minKeys ) {
85
+ continue ;
86
+ }
87
+
88
+ $ minKeys = $ keysCount ;
89
+ }
90
+
91
+ for ($ j = 0 ; $ j < $ minKeys ; $ j ++) {
92
+ $ types = [];
93
+ foreach ($ arrays as $ constantArray ) {
94
+ $ types [] = $ constantArray ->getValueTypes ()[$ j ];
95
+ }
96
+ $ arguments [] = [
97
+ $ arg ->value ,
98
+ TypeCombinator::union (...$ types ),
99
+ false ,
100
+ ];
101
+ }
102
+ } else {
103
+ $ arguments [] = [
104
+ $ arg ->value ,
105
+ $ type ->getIterableValueType (),
106
+ true ,
107
+ ];
108
+ }
109
+ continue ;
110
+ }
111
+
112
+ $ arguments [] = [
113
+ $ arg ->value ,
114
+ $ type ,
115
+ false ,
116
+ ];
117
+ }
118
+
119
+ $ errors = [];
120
+ $ invokedParametersCount = count ($ arguments );
121
+ foreach ($ arguments as $ i => [$ argumentValue , $ argumentValueType , $ unpack ]) {
122
+ if ($ unpack ) {
73
123
$ invokedParametersCount = max ($ functionParametersMinCount , $ functionParametersMaxCount );
74
124
break ;
75
125
}
@@ -115,13 +165,11 @@ public function check(
115
165
116
166
$ parameters = $ parametersAcceptor ->getParameters ();
117
167
118
- /** @var array<int, \PhpParser\Node\Arg> $args */
119
- $ args = $ funcCall ->args ;
120
- foreach ($ args as $ i => $ argument ) {
121
- if ($ this ->checkArgumentTypes && $ argument ->unpack ) {
168
+ foreach ($ arguments as $ i => [$ argumentValue , $ argumentValueType , $ unpack ]) {
169
+ if ($ this ->checkArgumentTypes && $ unpack ) {
122
170
$ iterableTypeResult = $ this ->ruleLevelHelper ->findTypeToCheck (
123
171
$ scope ,
124
- $ argument -> value ,
172
+ $ argumentValue ,
125
173
'' ,
126
174
static function (Type $ type ): bool {
127
175
return $ type ->isIterable ()->yes ();
@@ -154,11 +202,6 @@ static function (Type $type): bool {
154
202
}
155
203
156
204
$ parameterType = $ parameter ->getType ();
157
-
158
- $ argumentValueType = $ scope ->getType ($ argument ->value );
159
- if ($ argument ->unpack ) {
160
- $ argumentValueType = $ argumentValueType ->getIterableValueType ();
161
- }
162
205
if (
163
206
$ this ->checkArgumentTypes
164
207
&& !$ parameter ->passedByReference ()->createsNewVariable ()
@@ -177,10 +220,10 @@ static function (Type $type): bool {
177
220
if (
178
221
!$ this ->checkArgumentsPassedByReference
179
222
|| !$ parameter ->passedByReference ()->yes ()
180
- || $ argument -> value instanceof \PhpParser \Node \Expr \Variable
181
- || $ argument -> value instanceof \PhpParser \Node \Expr \ArrayDimFetch
182
- || $ argument -> value instanceof \PhpParser \Node \Expr \PropertyFetch
183
- || $ argument -> value instanceof \PhpParser \Node \Expr \StaticPropertyFetch
223
+ || $ argumentValue instanceof \PhpParser \Node \Expr \Variable
224
+ || $ argumentValue instanceof \PhpParser \Node \Expr \ArrayDimFetch
225
+ || $ argumentValue instanceof \PhpParser \Node \Expr \PropertyFetch
226
+ || $ argumentValue instanceof \PhpParser \Node \Expr \StaticPropertyFetch
184
227
) {
185
228
continue ;
186
229
}
0 commit comments