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

List refactoring (v6) #8773

Closed
wants to merge 114 commits into from
Closed
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
5b33589
Fix array_merge argument unpacking
danog Nov 20, 2022
cc4461f
Progress
danog Nov 20, 2022
3f5f98b
Refactoring
danog Nov 21, 2022
98bfe9f
Add note for 5.1 BC break
danog Nov 21, 2022
f7527d4
Fix
danog Nov 21, 2022
b632871
Remove getGenericArrayType parameter right away, and do not return a …
danog Nov 21, 2022
5797945
Skip test
danog Nov 21, 2022
7683a48
Revert
danog Nov 21, 2022
03c53d7
Deprecate TList and TNonEmptyList
danog Nov 21, 2022
80eab1b
Update
danog Nov 21, 2022
0a3971a
Deprecate TCallableList as well
danog Nov 21, 2022
5bf226d
Cleanup
danog Nov 21, 2022
bd9b77d
Cleanup
danog Nov 21, 2022
0345254
Reuse code
danog Nov 21, 2022
61a595b
Cleanup
danog Nov 21, 2022
9a5337d
Switch to Type::getListAtomic
danog Nov 22, 2022
d580181
Finalize
danog Nov 22, 2022
1a9edc3
Fix cs
danog Nov 22, 2022
4ceb562
Merge branch 'list_bc_break' into tnon_empty_list_refactoring
danog Nov 26, 2022
3154860
Cleanup
danog Nov 26, 2022
cf3a49a
Cleanup
danog Nov 26, 2022
d2e0d31
Cleanup
danog Nov 26, 2022
ab1a901
Cleanup
danog Nov 26, 2022
5b12042
Cleanup
danog Nov 26, 2022
b43207e
Cleanup
danog Nov 26, 2022
415d103
Cleanup
danog Nov 26, 2022
61cce1b
Cleanup
danog Nov 26, 2022
668afa9
Cleanup
danog Nov 26, 2022
956fa44
Cleanup
danog Nov 26, 2022
fca60dc
Cleanup
danog Nov 26, 2022
864a764
Cleanup
danog Nov 26, 2022
120ba1f
Refactoring
danog Nov 26, 2022
0a20c10
Refactoring
danog Nov 26, 2022
e2da9c9
Cleanup
danog Nov 26, 2022
3f710a8
Cleanup
danog Nov 26, 2022
546f2b3
Cleanup
danog Nov 26, 2022
51e6b11
Cleanup
danog Nov 26, 2022
700e066
Fixes
danog Nov 26, 2022
1c32a22
Fix
danog Nov 26, 2022
c823c87
Cleanup
danog Nov 26, 2022
45c26d0
Merge remote-tracking branch 'origin/master' into tnon_empty_list_ref…
danog Nov 26, 2022
d0d7c46
Fix
danog Nov 26, 2022
0f99365
Cleanup
danog Nov 26, 2022
a964715
Update
danog Nov 26, 2022
582a785
Fix
danog Nov 26, 2022
34825fc
Fix
danog Nov 26, 2022
156d629
Fix
danog Nov 26, 2022
a342a26
Fix
danog Nov 26, 2022
99bdc2f
Cleanup
danog Nov 26, 2022
65f849a
Cleanup
danog Nov 26, 2022
95e0614
Off-by-one fun
danog Nov 26, 2022
65886c9
Fixes
danog Nov 26, 2022
c0b0dbc
Fix bug
danog Nov 26, 2022
3178836
Fixes
danog Nov 26, 2022
e7f2fdd
Fix
danog Nov 26, 2022
e89609b
Fixes
danog Nov 26, 2022
0107a34
Cleanup logic
danog Nov 26, 2022
abbaf88
Improve assertions
danog Nov 26, 2022
a6a6595
Fix
danog Nov 26, 2022
4f10cd3
Cleanup
danog Nov 26, 2022
9a0ecf7
Fixes
danog Nov 26, 2022
909edb4
Fixes
danog Nov 27, 2022
2b9512e
Generate properly shaped lists when appending
danog Nov 27, 2022
0542b0e
Fix list templates
danog Nov 27, 2022
163330c
Merge remote-tracking branch 'o/master' into tnon_empty_list_refactoring
danog Nov 27, 2022
389c6e6
Revert weird legacy logic
danog Nov 27, 2022
0bb70b3
Update
danog Nov 27, 2022
8a4ba6d
Cleanup
danog Nov 28, 2022
c156807
Update
danog Nov 28, 2022
42380db
Cleanup
danog Nov 28, 2022
e821109
Fixes
danog Nov 29, 2022
2b0e979
Fixes
danog Nov 29, 2022
d24a243
Fix
danog Dec 1, 2022
8fc161c
Merge remote-tracking branch 'origin/master' into tnon_empty_list_ref…
danog Dec 1, 2022
6c3ffa2
Fix logic
danog Dec 1, 2022
03fc544
Fix
danog Dec 1, 2022
e529402
Fix
danog Dec 1, 2022
412d3c9
Simplify
danog Dec 1, 2022
e50b351
Cleanup
danog Dec 2, 2022
f89a3c9
Fix
danog Dec 2, 2022
54600ca
Fixup
danog Dec 2, 2022
4bf7087
Fixes
danog Dec 2, 2022
7e571f4
Fixes
danog Dec 2, 2022
b8ffb31
Possible fix
danog Dec 2, 2022
096b390
Cleanup
danog Dec 2, 2022
a0bf4cf
Fixes
danog Dec 2, 2022
8aecadd
Fixes
danog Dec 2, 2022
dead1be
Fix
danog Dec 2, 2022
c96a44d
Revert
danog Dec 2, 2022
a5df566
Fixes
danog Dec 2, 2022
b07aba9
Fix
danog Dec 2, 2022
e628c7b
Fixes
danog Dec 2, 2022
f015c50
Fixes
danog Dec 2, 2022
4847165
Fixes
danog Dec 2, 2022
c894b89
Fixes
danog Dec 2, 2022
550a7aa
Fixes
danog Dec 2, 2022
8f2a8c0
Fixes
danog Dec 2, 2022
d4df856
Cleanup
danog Dec 2, 2022
c802213
Fixes
danog Dec 2, 2022
59ab73f
Fixes
danog Dec 2, 2022
5f291af
Fixes
danog Dec 2, 2022
c05ce2d
Fixes
danog Dec 2, 2022
709324b
Fixes
danog Dec 2, 2022
4a77e83
Fix
danog Dec 2, 2022
c4c3016
Fix
danog Dec 2, 2022
abe722d
Fix array_key_exists with constants
danog Dec 2, 2022
23b0ac0
Hack
danog Dec 2, 2022
55edadc
Merge remote-tracking branch 'origin/master' into tnon_empty_list_ref…
danog Dec 2, 2022
78d64e3
Fix
danog Dec 2, 2022
3ce782f
Fixes
danog Dec 2, 2022
c24e6a9
Fix
danog Dec 2, 2022
4c47d50
Update
danog Dec 2, 2022
a154d96
Keep HasArrayKey logic only for class strings
danog Dec 2, 2022
4e10f16
cs-fix
danog Dec 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions UPGRADING.md
@@ -1,6 +1,12 @@
# Upgrading from Psalm 4 to Psalm 5
## Changed

- [BC] Psalm 5.1 will switch its internal representation of `list<T>` and `non-empty-list<T>` from the TList and TNonEmptyList classes to an unsealed list shape: the TList, TNonEmptyList and TCallableList classes will be removed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to follow semver and name that version 6.0

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually was hoping to get this merged by Psalm 5.0 given psalm/psalm.dev@dbe30c0, that description is outdated

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not gonna make it though, I'd still love to get at least #8728 merged so people already stop using TList and switch over to keyed lists in time for 5.1

Nothing will change for users: `list<T>` and `non-empty-list<T>` syntax will remain supported and its semantics unchanged.
Psalm 5.0 already deprecates the `TList`, `TNonEmptyList` and `TCallableList` classes: use `\Psalm\Type::getListAtomic`, `\Psalm\Type::getNonEmptyListAtomic` and `\Psalm\Type::getCallableListAtomic` to instantiate list atomics, or directly instantiate TKeyedArray objects with `is_list=true` where appropriate.

- [BC] The only optional boolean parameter of `TKeyedArray::getGenericArrayType` was removed, and will be replaced with a string parameter with a different meaning in Psalm 5.1.

- [BC] Shaped arrays can now be sealed: this brings many assertion improvements and bugfixes, see [the docs for more info](https://psalm.dev/docs/annotating_code/type_syntax/array_types/#sealed-object-like-arrays).

- [BC] All atomic types, `Psalm\Type\Union`, `Psalm\CodeLocation` and storages are fully immutable, use the new setter methods or the new constructors to change properties: these setter methods will return new instances without altering the original instance.
Expand Down
2 changes: 0 additions & 2 deletions docs/running_psalm/plugins/plugins_type_system.md
Expand Up @@ -185,8 +185,6 @@ foreach (range(1,1) as $_) $a[(string)rand(0,1)] = rand(0,1); // array<string,in

`TCallableArray` - denotes an array that is _also_ `callable`.

`TCallableList` - denotes a list that is _also_ `callable`.

`TCallableKeyedArray` - denotes an object-like array that is _also_ `callable`.

`TClassStringMap` - Represents an array where the type of each value is a function of its string key value
Expand Down
97 changes: 96 additions & 1 deletion psalm-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="dev-master@53e3889745852409b704e0035d93e0819d522912">
<files psalm-version="dev-master@12188caad4b2e4fd7a49a7199e97af2ba1d4c58e">
<file src="examples/TemplateChecker.php">
<PossiblyUndefinedIntArrayOffset occurrences="2">
<code>$comment_block-&gt;tags['variablesfrom'][0]</code>
Expand Down Expand Up @@ -72,6 +72,9 @@
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php">
<DeprecatedClass occurrences="1">
<code>new Atomic\TList(Type::getMixed())</code>
</DeprecatedClass>
<PossiblyUndefinedIntArrayOffset occurrences="28">
<code>$assertion-&gt;rule[0]</code>
<code>$assertion-&gt;rule[0]</code>
Expand Down Expand Up @@ -103,6 +106,13 @@
<code>$gettype_expr-&gt;getArgs()[0]</code>
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php">
<DeprecatedClass occurrences="3">
<code>$array_atomic_type</code>
<code>$array_atomic_type</code>
<code>$array_atomic_type</code>
</DeprecatedClass>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php">
<PossiblyUndefinedIntArrayOffset occurrences="2">
<code>$invalid_left_messages[0]</code>
Expand All @@ -113,18 +123,34 @@
<ComplexMethod occurrences="1">
<code>verifyType</code>
</ComplexMethod>
<DeprecatedClass occurrences="4">
<code>$array_type</code>
<code>$unpacked_atomic_array</code>
<code>TKeyedArray|TArray|TClassStringMap</code>
<code>TKeyedArray|TArray|TClassStringMap|null</code>
</DeprecatedClass>
<PossiblyUndefinedIntArrayOffset occurrences="3">
<code>$non_existent_method_ids[0]</code>
<code>$parts[1]</code>
<code>explode('::', $cased_method_id)[1]</code>
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php">
<DeprecatedClass occurrences="2">
<code>$array_type</code>
<code>$array_type</code>
</DeprecatedClass>
<PossiblyUndefinedIntArrayOffset occurrences="1">
<code>$arg_function_params[$argument_offset][0]</code>
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php">
<DeprecatedClass occurrences="4">
<code>$array_arg_type</code>
<code>$array_type</code>
<code>$array_type</code>
<code>$replacement_array_type</code>
</DeprecatedClass>
<PossiblyUndefinedIntArrayOffset occurrences="4">
<code>$args[0]</code>
<code>$args[0]</code>
Expand Down Expand Up @@ -155,6 +181,14 @@
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php">
<DeprecatedClass occurrences="6">
<code>$array_type</code>
<code>$array_type</code>
<code>$type</code>
<code>$type</code>
<code>TArray|TKeyedArray|TClassStringMap</code>
<code>TList</code>
</DeprecatedClass>
<ReferenceConstraintViolation occurrences="3">
<code>$stmt_type</code>
<code>$stmt_type</code>
Expand Down Expand Up @@ -192,6 +226,9 @@
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Codebase/InternalCallMapHandler.php">
<DeprecatedClass occurrences="1">
<code>$array_atomic_type</code>
</DeprecatedClass>
<PossiblyUndefinedIntArrayOffset occurrences="2">
<code>$callables[0]</code>
<code>$callables[0]</code>
Expand Down Expand Up @@ -286,6 +323,9 @@
</RedundantCondition>
</file>
<file src="src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php">
<DeprecatedClass occurrences="1">
<code>$array_type</code>
</DeprecatedClass>
<PossiblyUndefinedIntArrayOffset occurrences="1">
<code>$flow_parts[0]</code>
</PossiblyUndefinedIntArrayOffset>
Expand All @@ -300,6 +340,33 @@
<code>$cs[0]</code>
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php">
<DeprecatedClass occurrences="1">
<code>$array_arg_type</code>
</DeprecatedClass>
</file>
<file src="src/Psalm/Internal/Scanner/PhpStormMetaScanner.php">
<DeprecatedClass occurrences="2">
<code>$array_atomic_type</code>
<code>$array_atomic_type</code>
</DeprecatedClass>
</file>
<file src="src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php">
<DeprecatedClass occurrences="6">
<code>$container_type_part</code>
<code>$container_type_part</code>
<code>$input_type_part</code>
<code>$input_type_part</code>
<code>TArray|TKeyedArray|TClassStringMap</code>
<code>TArray|TKeyedArray|TClassStringMap</code>
</DeprecatedClass>
</file>
<file src="src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php">
<DeprecatedClass occurrences="2">
<code>TList::class</code>
<code>TList::class</code>
</DeprecatedClass>
</file>
<file src="src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php">
<LessSpecificReturnStatement occurrences="1">
<code>$callable</code>
Expand All @@ -308,13 +375,19 @@
<code>TCallable|TClosure|null</code>
</MoreSpecificReturnType>
</file>
<file src="src/Psalm/Internal/Type/SimpleAssertionReconciler.php">
<DeprecatedClass occurrences="3">
<code>TList::class</code>
</DeprecatedClass>
</file>
<file src="src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php">
<ImpureMethodCall occurrences="2">
<code>getClassTemplateTypes</code>
<code>has</code>
</ImpureMethodCall>
</file>
<file src="src/Psalm/Internal/Type/TypeCombiner.php">
<DeprecatedClass occurrences="1"/>
<PossiblyUndefinedIntArrayOffset occurrences="6">
<code>$combination-&gt;array_type_params[1]</code>
<code>$combination-&gt;array_type_params[1]</code>
Expand Down Expand Up @@ -349,6 +422,13 @@
<code>traverse</code>
</ImpureMethodCall>
</file>
<file src="src/Psalm/Type.php">
<DeprecatedClass occurrences="3">
<code>new TCallableList($of, null, null, $from_docblock)</code>
<code>new TList($of, $from_docblock)</code>
<code>new TNonEmptyList($of, null, null, $from_docblock)</code>
</DeprecatedClass>
</file>
<file src="src/Psalm/Type/Atomic.php">
<ImpureMethodCall occurrences="12">
<code>classExtendsOrImplements</code>
Expand Down Expand Up @@ -394,6 +474,11 @@
<code>getMostSpecificTypeFromBounds</code>
</ImpureMethodCall>
</file>
<file src="src/Psalm/Type/Atomic/TCallableList.php">
<DeprecatedClass occurrences="1">
<code>TNonEmptyList</code>
</DeprecatedClass>
</file>
<file src="src/Psalm/Type/Atomic/TClassString.php">
<ImpureMethodCall occurrences="1">
<code>replace</code>
Expand Down Expand Up @@ -441,6 +526,11 @@
<code>$cloned-&gt;type_param</code>
</ImpurePropertyAssignment>
</file>
<file src="src/Psalm/Type/Atomic/TNonEmptyList.php">
<DeprecatedClass occurrences="1">
<code>TList</code>
</DeprecatedClass>
</file>
<file src="src/Psalm/Type/Atomic/TObjectWithProperties.php">
<ImpureMethodCall occurrences="2">
<code>replace</code>
Expand Down Expand Up @@ -506,6 +596,11 @@
<code>allFloatLiterals</code>
</PossiblyUnusedMethod>
</file>
<file src="tests/Config/Plugin/Hook/CustomArrayMapFunctionStorageProvider.php">
<DeprecatedClass occurrences="1">
<code>new Type\Atomic\TList($last_callable_arg-&gt;return_type ?? Type::getMixed())</code>
</DeprecatedClass>
</file>
<file src="tests/Internal/Codebase/InternalCallMapHandlerTest.php">
<UnusedPsalmSuppress occurrences="1">
<code>UndefinedMethod</code>
Expand Down
Expand Up @@ -13,7 +13,6 @@
use Psalm\Type\Atomic\TGenericObject;
use Psalm\Type\Atomic\TIterable;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Atomic\TList;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Union;

Expand Down Expand Up @@ -263,10 +262,6 @@ private static function processYieldTypes(
$type = $type->getGenericArrayType();
}

if ($type instanceof TList) {
$type = new TArray([Type::getInt(), $type->type_param]);
}

if ($type instanceof TArray) {
[$key_type_param, $value_type_param] = $type->type_params;

Expand Down
3 changes: 1 addition & 2 deletions src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php
Expand Up @@ -54,7 +54,6 @@
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TClosure;
use Psalm\Type\Atomic\TGenericObject;
use Psalm\Type\Atomic\TList;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TTemplateParam;
Expand Down Expand Up @@ -1161,7 +1160,7 @@ private function processParams(
]);
} else {
$var_type = new Union([
new TList($param_type),
Type::getListAtomic($param_type),
], [
'by_ref' => $function_param->by_ref,
'parent_nodes' => $parent_nodes
Expand Down
46 changes: 8 additions & 38 deletions src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php
Expand Up @@ -40,18 +40,14 @@
use Psalm\Type\Atomic;
use Psalm\Type\Atomic\Scalar;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TDependentListKey;
use Psalm\Type\Atomic\TFalse;
use Psalm\Type\Atomic\TGenericObject;
use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TIterable;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Atomic\TList;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNever;
use Psalm\Type\Atomic\TNonEmptyArray;
use Psalm\Type\Atomic\TNonEmptyList;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TObjectWithProperties;
Expand Down Expand Up @@ -390,8 +386,6 @@ public static function analyze(
/**
* @param PhpParser\Node\Stmt\Foreach_|PhpParser\Node\Expr\YieldFrom $stmt
*
* @psalm-suppress ComplexMethod
*
* @return false|null
*/
public static function checkIteratorType(
Expand Down Expand Up @@ -467,43 +461,19 @@ public static function checkIteratorType(

if ($iterator_atomic_type instanceof TArray
|| $iterator_atomic_type instanceof TKeyedArray
|| $iterator_atomic_type instanceof TList
) {
if ($iterator_atomic_type instanceof TKeyedArray) {
if ($iterator_atomic_type->fallback_params === null) {
$all_possibly_undefined = true;
foreach ($iterator_atomic_type->properties as $prop) {
if (!$prop->possibly_undefined) {
$all_possibly_undefined = false;
break;
}
}
if ($all_possibly_undefined) {
$always_non_empty_array = false;
}
} else {
$always_non_empty_array = false;
}
$iterator_atomic_type = $iterator_atomic_type->getGenericArrayType();
} elseif ($iterator_atomic_type instanceof TList) {
$list_var_id = ExpressionIdentifier::getExtendedVarId(
$expr,
$statements_analyzer->getFQCLN(),
$statements_analyzer
);

if (!$iterator_atomic_type instanceof TNonEmptyList) {
if (!$iterator_atomic_type->isNonEmpty()) {
$always_non_empty_array = false;
}

$iterator_atomic_type = new TArray([
$list_var_id
? new Union([
new TDependentListKey($list_var_id)
])
: new Union([new TIntRange(0, null)]),
$iterator_atomic_type->type_param
]);
$iterator_atomic_type = $iterator_atomic_type->getGenericArrayType(
ExpressionIdentifier::getExtendedVarId(
$expr,
$statements_analyzer->getFQCLN(),
$statements_analyzer
)
);
} elseif (!$iterator_atomic_type instanceof TNonEmptyArray) {
$always_non_empty_array = false;
}
Expand Down
Expand Up @@ -29,14 +29,12 @@
use Psalm\Type\Atomic\TFloat;
use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Atomic\TList;
use Psalm\Type\Atomic\TLiteralClassString;
use Psalm\Type\Atomic\TLiteralFloat;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNonEmptyArray;
use Psalm\Type\Atomic\TNonEmptyList;
use Psalm\Type\Atomic\TObjectWithProperties;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateParam;
Expand Down Expand Up @@ -146,9 +144,9 @@ public static function analyze(

if ($array_creation_info->all_list) {
if ($array_creation_info->can_be_empty) {
$array_type = new TList($item_value_type ?? Type::getMixed());
$array_type = Type::getListAtomic($item_value_type ?? Type::getMixed());
} else {
$array_type = new TNonEmptyList($item_value_type ?? Type::getMixed());
$array_type = Type::getNonEmptyListAtomic($item_value_type ?? Type::getMixed());
}

$stmt_type = new Union([
Expand Down Expand Up @@ -552,7 +550,7 @@ private static function handleUnpackedArray(
} else {
$codebase = $statements_analyzer->getCodebase();

if (!$unpacked_atomic_type instanceof TNonEmptyList
if (!($unpacked_atomic_type instanceof TKeyedArray && $unpacked_atomic_type->isNonEmpty())
&& !$unpacked_atomic_type instanceof TNonEmptyArray
) {
$all_non_empty = false;
Expand Down