Repository: phpactor/phpactor (php)

Issue:
Lost type in array shape
```php
<?php

/** @return array{name: string, data: list<string>} */
function data() {
	// ...
}

$data = data();

$list = $data['data']; // $list must be `list<string>` but `mixed` given
```

Pull request description:
Gh 1671
Fixes #1671 

Patch:
diff --git a/lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php
index 83a3af1d..e6211382 100644
--- a/lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php
+++ b/lib/WorseReflection/Core/Inference/Resolver/AssignmentExpressionResolver.php
@@ -25,8 +25,8 @@ use Microsoft\PhpParser\Node\Statement\ExpressionStatement;
 use Phpactor\WorseReflection\Core\Type;
 use Phpactor\WorseReflection\Core\TypeFactory;
 use Phpactor\WorseReflection\Core\Type\AggregateType;
+use Phpactor\WorseReflection\Core\Type\ArrayAccessType;
 use Phpactor\WorseReflection\Core\Type\ArrayLiteral;
-use Phpactor\WorseReflection\Core\Type\ArrayShapeType;
 use Phpactor\WorseReflection\Core\Type\ArrayType;
 use Phpactor\WorseReflection\Core\Type\Literal;
 use Phpactor\WorseReflection\Core\Type\MissingType;
@@ -260,11 +260,7 @@ class AssignmentExpressionResolver implements Resolver
 
     private function offsetType(Type $type, int $index): Type
     {
-        if ($type instanceof ArrayShapeType) {
-            return $type->typeAtOffset($index);
-        }
-
-        if ($type instanceof ArrayLiteral) {
+        if ($type instanceof ArrayAccessType) {
             return $type->typeAtOffset($index);
         }
 
diff --git a/lib/WorseReflection/Core/Inference/Resolver/SubscriptExpressionResolver.php b/lib/WorseReflection/Core/Inference/Resolver/SubscriptExpressionResolver.php
index 8a94a998..ffd31f2f 100644
--- a/lib/WorseReflection/Core/Inference/Resolver/SubscriptExpressionResolver.php
+++ b/lib/WorseReflection/Core/Inference/Resolver/SubscriptExpressionResolver.php
@@ -9,7 +9,7 @@ use Phpactor\WorseReflection\Core\Inference\Frame;
 use Phpactor\WorseReflection\Core\Inference\NodeContext;
 use Phpactor\WorseReflection\Core\Inference\Resolver;
 use Phpactor\WorseReflection\Core\Inference\NodeContextResolver;
-use Phpactor\WorseReflection\Core\Type\ArrayLiteral;
+use Phpactor\WorseReflection\Core\Type\ArrayAccessType;
 use Phpactor\WorseReflection\Core\Type\ArrayType;
 use Phpactor\WorseReflection\TypeUtil;
 
@@ -42,7 +42,7 @@ class SubscriptExpressionResolver implements Resolver
         $arrayLiteralType = $info->type();
         $info = $info->withType($type->valueType);
 
-        if (!$arrayLiteralType instanceof ArrayLiteral) {
+        if (!$arrayLiteralType instanceof ArrayAccessType) {
             $info = $info->withIssue(sprintf(
                 'Array value for symbol "%s" is not an array, is a "%s"',
                 (string) $info->symbol(),
diff --git a/lib/WorseReflection/Core/Type/ArrayAccessType.php b/lib/WorseReflection/Core/Type/ArrayAccessType.php
new file mode 100644
index 00000000..a1725afb
--- /dev/null
+++ b/lib/WorseReflection/Core/Type/ArrayAccessType.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Phpactor\WorseReflection\Core\Type;
+
+use Phpactor\WorseReflection\Core\Type;
+
+interface ArrayAccessType
+{
+    /**
+     * @param array-key $offset $offset
+     */
+    public function typeAtOffset($offset): Type;
+}
diff --git a/lib/WorseReflection/Core/Type/ArrayLiteral.php b/lib/WorseReflection/Core/Type/ArrayLiteral.php
index ab418dde..bef3ea84 100644
--- a/lib/WorseReflection/Core/Type/ArrayLiteral.php
+++ b/lib/WorseReflection/Core/Type/ArrayLiteral.php
@@ -6,7 +6,7 @@ use Phpactor\WorseReflection\Core\Type;
 use Phpactor\WorseReflection\Core\TypeFactory;
 use Phpactor\WorseReflection\TypeUtil;
 
-class ArrayLiteral extends ArrayType implements Literal, Generalizable
+class ArrayLiteral extends ArrayType implements Literal, Generalizable, ArrayAccessType
 {
     /**
      * @var array<array-key,Type>
diff --git a/lib/WorseReflection/Core/Type/ArrayShapeType.php b/lib/WorseReflection/Core/Type/ArrayShapeType.php
index 7037d775..393d68b7 100644
--- a/lib/WorseReflection/Core/Type/ArrayShapeType.php
+++ b/lib/WorseReflection/Core/Type/ArrayShapeType.php
@@ -7,7 +7,7 @@ use Phpactor\WorseReflection\Core\Type;
 use Phpactor\WorseReflection\Core\TypeFactory;
 use Phpactor\WorseReflection\TypeUtil;
 
-class ArrayShapeType extends ArrayType implements Generalizable
+class ArrayShapeType extends ArrayType implements Generalizable, ArrayAccessType
 {
     /**
      * @var array<array-key,Type>


Test patch:
diff --git a/lib/WorseReflection/Tests/Inference/subscript-expression/array_shape_access.test b/lib/WorseReflection/Tests/Inference/subscript-expression/array_shape_access.test
new file mode 100644
index 00000000..b1d7e14c
--- /dev/null
+++ b/lib/WorseReflection/Tests/Inference/subscript-expression/array_shape_access.test
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @var array<string,string>
+ */
+$arr = ['hello' => 'world'];
+
+/** @return array{name: string, data: list<string>} */
+function data()
+{
+    // ...
+}
+
+
+$data = data();
+
+$list = $data['data'];
+wrAssertType('array<int,string>', $list);