diff --git a/src/Psalm/Checker/Statements/Expression/Fetch/ArrayFetchChecker.php b/src/Psalm/Checker/Statements/Expression/Fetch/ArrayFetchChecker.php index 61a3a5a5d..7fe8e4618 100644 --- a/src/Psalm/Checker/Statements/Expression/Fetch/ArrayFetchChecker.php +++ b/src/Psalm/Checker/Statements/Expression/Fetch/ArrayFetchChecker.php @@ -441,6 +441,8 @@ class ArrayFetchChecker } } } else { + $generic_key_type = $type->getGenericKeyType(); + if ($key_value !== null) { if (isset($type->properties[$key_value]) || $replacement_type) { $has_valid_offset = true; @@ -492,10 +494,12 @@ class ArrayFetchChecker $array_access_type = Type::getMixed(); } - } elseif (TypeChecker::isContainedBy( + } elseif ((TypeChecker::isContainedBy( $codebase, $offset_type, - $type->getGenericKeyType(), + $generic_key_type->isMixed() + ? new Type\Union([ new TInt, new TString ]) + : $generic_key_type, true, $offset_type->ignore_falsable_issues, $has_scalar_match, @@ -505,7 +509,8 @@ class ArrayFetchChecker $type_coerced_from_scalar ) || $type_coerced_from_scalar - || $in_assignment + || $in_assignment) + && !$to_string_cast ) { if ($replacement_type) { $generic_params = Type::combineUnionTypes( @@ -514,7 +519,7 @@ class ArrayFetchChecker ); $new_key_type = Type::combineUnionTypes( - $type->getGenericKeyType(), + $generic_key_type, $offset_type ); @@ -552,7 +557,7 @@ class ArrayFetchChecker $has_valid_offset = true; } else { if (!$inside_isset || $type->sealed) { - $expected_offset_types[] = (string)$type->getGenericKeyType()->getId(); + $expected_offset_types[] = (string)$generic_key_type->getId(); } $array_access_type = Type::getMixed(); diff --git a/tests/ArrayAccessTest.php b/tests/ArrayAccessTest.php index c12133bc7..260205728 100644 --- a/tests/ArrayAccessTest.php +++ b/tests/ArrayAccessTest.php @@ -265,6 +265,18 @@ class ArrayAccessTest extends TestCase }', 'error_message' => 'InvalidArrayOffset', ], + 'toStringOffset' => [ + ' "bar"]; + echo $a[new Foo];', + 'error_message' => 'InvalidArrayOffset', + ], ]; } }