Skip to content

Commit b9741cb

Browse files
committed
Fix
1 parent 438096e commit b9741cb

File tree

4 files changed

+93
-106
lines changed

4 files changed

+93
-106
lines changed

src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php

-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
use Psalm\Type;
4545
use Psalm\Type\Atomic\TArray;
4646
use Psalm\Type\Atomic\TConditional;
47-
use Psalm\Type\Atomic\TKeyedArray;
4847
use Psalm\Type\Atomic\TNull;
4948
use Psalm\Type\Atomic\TTemplateParam;
5049
use Psalm\Type\TaintKindGroup;

src/Psalm/Internal/Provider/ParamsProvider/ArrayFilterParamsProvider.php

+9-22
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
use Psalm\Plugin\EventHandler\FunctionParamsProviderInterface;
1717
use Psalm\Storage\FunctionLikeParameter;
1818
use Psalm\Type;
19-
use Psalm\Type\Atomic\TArray;
2019
use Psalm\Type\Atomic\TCallable;
21-
use Psalm\Type\Atomic\TKeyedArray;
2220
use Psalm\Type\Union;
2321

2422
use function strtolower;
@@ -79,11 +77,12 @@ public static function getFunctionParams(FunctionParamsProviderEvent $event): ?a
7977

8078
return null;
8179
}
80+
$codebase = $statements_source->getCodebase();
8281

8382
// currently only supports literal types and variables (but not function calls)
8483
// due to https://github.com/vimeo/psalm/issues/8905
8584
$first_arg_type = SimpleTypeInferer::infer(
86-
$statements_source->getCodebase(),
85+
$codebase,
8786
$statements_source->node_data,
8887
$call_args[0]->value,
8988
$statements_source->getAliases(),
@@ -100,30 +99,18 @@ public static function getFunctionParams(FunctionParamsProviderEvent $event): ?a
10099
$first_arg_type = $event->getContext()->vars_in_scope[$extended_var_id] ?? null;
101100
}
102101

103-
$fallback = new TArray([Type::getArrayKey(), Type::getMixed()]);
104-
if (!$first_arg_type || $first_arg_type->isMixed()) {
105-
$first_arg_array = $fallback;
102+
if (!$first_arg_type || $first_arg_type->isMixed() || !$first_arg_type->hasArray()) {
103+
$key_type = Type::getArrayKey();
104+
$inner_type = Type::getMixed();
106105
} else {
107-
$first_arg_array = $first_arg_type->hasArray()
108-
&& ($array_atomic_type = $first_arg_type->getArray())
109-
&& ($array_atomic_type instanceof TArray
110-
|| $array_atomic_type instanceof TKeyedArray)
111-
? $array_atomic_type
112-
: $fallback;
113-
}
114-
115-
if ($first_arg_array instanceof TArray) {
116-
$inner_type = $first_arg_array->type_params[1];
117-
$key_type = $first_arg_array->type_params[0];
118-
} else {
119-
$inner_type = $first_arg_array->getGenericValueType();
120-
$key_type = $first_arg_array->getGenericKeyType();
106+
$inner_type = Type::combineUnionTypeArray($first_arg_type->getArrayValueTypes(), $codebase);
107+
$key_type = Type::combineUnionTypeArray($first_arg_type->getArrayKeyTypes(), $codebase);
121108
}
122109

123110
$has_both = false;
124111
if (isset($call_args[2])) {
125112
$mode_type = SimpleTypeInferer::infer(
126-
$statements_source->getCodebase(),
113+
$codebase,
127114
$statements_source->node_data,
128115
$call_args[2]->value,
129116
$statements_source->getAliases(),
@@ -168,7 +155,7 @@ public static function getFunctionParams(FunctionParamsProviderEvent $event): ?a
168155
$inner_type = Type::combineUnionTypes(
169156
$inner_type,
170157
$key_type,
171-
$statements_source->getCodebase(),
158+
$codebase,
172159
);
173160

174161
continue;

src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php

+84-82
Original file line numberDiff line numberDiff line change
@@ -63,107 +63,109 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev
6363
$fallback = new TArray([Type::getArrayKey(), Type::getMixed()]);
6464
$array_arg = $call_args[0]->value ?? null;
6565
if (!$array_arg) {
66-
$first_arg_array = $fallback;
66+
$first_arg_arrays = [$fallback];
6767
} else {
6868
$first_arg_type = $statements_source->node_data->getType($array_arg);
69-
if (!$first_arg_type || $first_arg_type->isMixed()) {
70-
$first_arg_array = $fallback;
69+
if (!$first_arg_type || $first_arg_type->isMixed() || !($arrays = $first_arg_type->getArrays())) {
70+
$first_arg_arrays = [$fallback];
7171
} else {
72-
$first_arg_array = $first_arg_type->hasArray()
73-
&& ($array_atomic_type = $first_arg_type->getArray())
74-
&& ($array_atomic_type instanceof TArray
75-
|| $array_atomic_type instanceof TKeyedArray)
76-
? $array_atomic_type
77-
: $fallback;
72+
$first_arg_arrays = $arrays;
7873
}
7974
}
8075

81-
if ($first_arg_array instanceof TArray) {
82-
$inner_type = $first_arg_array->type_params[1];
83-
$key_type = $first_arg_array->type_params[0];
84-
} else {
85-
$inner_type = $first_arg_array->getGenericValueType();
86-
$key_type = $first_arg_array->getGenericKeyType();
87-
88-
if (!isset($call_args[1]) && $first_arg_array->fallback_params === null) {
89-
$had_one = count($first_arg_array->properties) === 1;
90-
91-
$new_properties = array_filter(
92-
array_map(
93-
static function ($keyed_type) use ($statements_source, $context) {
94-
$prev_keyed_type = $keyed_type;
95-
96-
$keyed_type = AssertionReconciler::reconcile(
97-
new Truthy(),
98-
$keyed_type,
99-
'',
100-
$statements_source,
101-
$context->inside_loop,
102-
[],
103-
null,
104-
$statements_source->getSuppressedIssues(),
105-
);
76+
$out_types = [];
77+
foreach ($first_arg_arrays as $first_arg_array) {
78+
if ($first_arg_array instanceof TArray) {
79+
$inner_type = $first_arg_array->type_params[1];
80+
$key_type = $first_arg_array->type_params[0];
81+
} else {
82+
$inner_type = $first_arg_array->getGenericValueType();
83+
$key_type = $first_arg_array->getGenericKeyType();
84+
85+
if (!isset($call_args[1]) && $first_arg_array->fallback_params === null) {
86+
$had_one = count($first_arg_array->properties) === 1;
87+
88+
$new_properties = array_filter(
89+
array_map(
90+
static function ($keyed_type) use ($statements_source, $context) {
91+
$prev_keyed_type = $keyed_type;
92+
93+
$keyed_type = AssertionReconciler::reconcile(
94+
new Truthy(),
95+
$keyed_type,
96+
'',
97+
$statements_source,
98+
$context->inside_loop,
99+
[],
100+
null,
101+
$statements_source->getSuppressedIssues(),
102+
);
103+
104+
return $keyed_type->setPossiblyUndefined(!$prev_keyed_type->isAlwaysTruthy());
105+
},
106+
$first_arg_array->properties,
107+
),
108+
static fn($keyed_type) => !$keyed_type->isNever()
109+
);
106110

107-
return $keyed_type->setPossiblyUndefined(!$prev_keyed_type->isAlwaysTruthy());
108-
},
109-
$first_arg_array->properties,
110-
),
111-
static fn($keyed_type) => !$keyed_type->isNever()
112-
);
111+
if (!$new_properties) {
112+
$out_types []= Type::getEmptyArrayAtomic();
113+
continue;
114+
}
113115

114-
if (!$new_properties) {
115-
return Type::getEmptyArray();
116+
$out_types []= new TKeyedArray(
117+
$new_properties,
118+
null,
119+
$first_arg_array->fallback_params,
120+
$first_arg_array->is_list && $had_one,
121+
);
116122
}
117-
118-
return new Union([new TKeyedArray(
119-
$new_properties,
120-
null,
121-
$first_arg_array->fallback_params,
122-
$first_arg_array->is_list && $had_one,
123-
)]);
124123
}
125-
}
126124

127-
if (!isset($call_args[1])) {
128-
$inner_type = AssertionReconciler::reconcile(
129-
new Truthy(),
130-
$inner_type,
131-
'',
132-
$statements_source,
133-
$context->inside_loop,
134-
[],
135-
null,
136-
$statements_source->getSuppressedIssues(),
137-
);
138-
139-
if ($first_arg_array instanceof TKeyedArray
140-
&& $first_arg_array->is_list
141-
&& $key_type->isSingleIntLiteral()
142-
&& $key_type->getSingleIntLiteral()->value === 0
143-
) {
144-
return Type::getList(
125+
if (!isset($call_args[1])) {
126+
$inner_type = AssertionReconciler::reconcile(
127+
new Truthy(),
145128
$inner_type,
129+
'',
130+
$statements_source,
131+
$context->inside_loop,
132+
[],
133+
null,
134+
$statements_source->getSuppressedIssues(),
146135
);
147-
}
148136

149-
if ($key_type->getLiteralStrings()) {
150-
$key_type = $key_type->getBuilder()->addType(new TString)->freeze();
151-
}
137+
if ($first_arg_array instanceof TKeyedArray
138+
&& $first_arg_array->is_list
139+
&& $key_type->isSingleIntLiteral()
140+
&& $key_type->getSingleIntLiteral()->value === 0
141+
) {
142+
$out_types []= Type::getListAtomic(
143+
$inner_type,
144+
);
145+
continue;
146+
}
152147

153-
if ($key_type->getLiteralInts()) {
154-
$key_type = $key_type->getBuilder()->addType(new TInt)->freeze();
155-
}
148+
if ($inner_type->isUnionEmpty()) {
149+
$out_types []= Type::getEmptyArrayAtomic();
150+
}
156151

157-
if ($inner_type->isUnionEmpty()) {
158-
return Type::getEmptyArray();
159-
}
152+
if ($key_type->getLiteralStrings()) {
153+
$key_type = $key_type->getBuilder()->addType(new TString)->freeze();
154+
}
160155

161-
return new Union([
162-
new TArray([
156+
if ($key_type->getLiteralInts()) {
157+
$key_type = $key_type->getBuilder()->addType(new TInt)->freeze();
158+
}
159+
160+
$out_types []= new TArray([
163161
$key_type,
164162
$inner_type,
165-
]),
166-
]);
163+
]);
164+
}
165+
}
166+
167+
if ($out_types) {
168+
return new Union([$out_types]);
167169
}
168170

169171
if (!isset($call_args[2])) {

src/Psalm/Type/UnionTrait.php

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use InvalidArgumentException;
88
use Psalm\CodeLocation;
99
use Psalm\Codebase;
10-
use Psalm\Internal\Type\TypeCombiner;
1110
use Psalm\Internal\TypeVisitor\CanContainObjectTypeVisitor;
1211
use Psalm\Internal\TypeVisitor\ClasslikeReplacer;
1312
use Psalm\Internal\TypeVisitor\ContainsClassLikeVisitor;

0 commit comments

Comments
 (0)