Skip to content
Permalink
Browse files

Allow array access on @template T as array

Ref #1607
  • Loading branch information...
muglug committed May 23, 2019
1 parent 01dcf8f commit 56daa390fcd11582bc4a30342cd6af0c622db2c2
@@ -105,6 +105,7 @@ public static function analyze(
if ($keyed_array_var_id
&& $context->hasVariable($keyed_array_var_id)
&& !$context->vars_in_scope[$keyed_array_var_id]->possibly_undefined
&& !$context->vars_in_scope[$keyed_array_var_id]->isVanillaMixed()
) {
$stmt->inferredType = clone $context->vars_in_scope[$keyed_array_var_id];
@@ -197,7 +198,10 @@ public static function analyze(
}
}
if ($keyed_array_var_id && $context->hasVariable($keyed_array_var_id, $statements_analyzer)) {
if ($keyed_array_var_id
&& $context->hasVariable($keyed_array_var_id, $statements_analyzer)
&& (!isset($stmt->inferredType) || $stmt->inferredType->isVanillaMixed())
) {
$stmt->inferredType = $context->vars_in_scope[$keyed_array_var_id];
}
@@ -316,7 +320,51 @@ public static function getArrayAccessTypeGivenOffset(
}
}
foreach ($array_type->getTypes() as &$type) {
foreach ($array_type->getTypes() as $type_string => $type) {
if ($type instanceof TMixed || $type instanceof TTemplateParam || $type instanceof TEmpty) {
if (!$type instanceof TTemplateParam || $type->as->isMixed() || !$type->as->isSingle()) {
if (!$context->collect_initializations
&& !$context->collect_mutations
&& $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
&& (!(($parent_source = $statements_analyzer->getSource())
instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer)
|| !$parent_source->getSource() instanceof \Psalm\Internal\Analyzer\TraitAnalyzer)
) {
$codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
}
if (!$context->inside_isset) {
if ($in_assignment) {
if (IssueBuffer::accepts(
new MixedArrayAssignment(
'Cannot access array value on mixed variable ' . $array_var_id,
new CodeLocation($statements_analyzer->getSource(), $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
} else {
if (IssueBuffer::accepts(
new MixedArrayAccess(
'Cannot access array value on mixed variable ' . $array_var_id,
new CodeLocation($statements_analyzer->getSource(), $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
}
}
$has_valid_offset = true;
$array_access_type = Type::getMixed();
break;
}
$type = array_values($type->as->getTypes())[0];
}
if ($type instanceof TNull) {
if ($array_type->ignore_nullable_issues) {
continue;
@@ -376,8 +424,9 @@ public static function getArrayAccessTypeGivenOffset(
&& $key_value !== null
) {
// ok, type becomes an ObjectLike
$array_type->removeType($type_string);
$type = new ObjectLike([$key_value => new Type\Union([new TEmpty])]);
$array_type->addType($type);
}
$offset_type = self::replaceOffsetTypeWithInts($offset_type);
@@ -583,16 +632,20 @@ public static function getArrayAccessTypeGivenOffset(
if (!$stmt->dim && $property_count) {
++$property_count;
$array_type->removeType($type_string);
$type = new TNonEmptyArray([
$new_key_type,
$generic_params,
]);
$array_type->addType($type);
$type->count = $property_count;
} else {
$array_type->removeType($type_string);
$type = new TArray([
$new_key_type,
$generic_params,
]);
$array_type->addType($type);
}
if (!$array_access_type) {
@@ -707,46 +760,6 @@ public static function getArrayAccessTypeGivenOffset(
continue;
}
if ($type instanceof TMixed || $type instanceof TTemplateParam || $type instanceof TEmpty) {
if (!$context->collect_initializations
&& !$context->collect_mutations
&& $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
&& (!(($parent_source = $statements_analyzer->getSource())
instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer)
|| !$parent_source->getSource() instanceof \Psalm\Internal\Analyzer\TraitAnalyzer)
) {
$codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
}
if (!$context->inside_isset) {
if ($in_assignment) {
if (IssueBuffer::accepts(
new MixedArrayAssignment(
'Cannot access array value on mixed variable ' . $array_var_id,
new CodeLocation($statements_analyzer->getSource(), $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
} else {
if (IssueBuffer::accepts(
new MixedArrayAccess(
'Cannot access array value on mixed variable ' . $array_var_id,
new CodeLocation($statements_analyzer->getSource(), $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
}
}
$has_valid_offset = true;
$array_access_type = Type::getMixed();
break;
}
if (!$context->collect_initializations
&& !$context->collect_mutations
&& $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
@@ -63,7 +63,7 @@ class A {
/** @psalm-suppress UndefinedClass */
if (!isset($a->arr["bat"]) || strlen($a->arr["bat"])) { }',
'assertions' => [],
'error_levels' => ['MixedArgument'],
'error_levels' => ['MixedArgument', 'MixedArrayAccess'],
],
'notEmptyIntOffset' => [
'<?php
@@ -188,9 +188,9 @@ class B {}
'implicitIndexedIntArrayCreation' => [
'<?php
$foo = [];
$foo[0] = "hello";
$foo[1] = "hello";
$foo[2] = "hello";
$foo[0] = "a";
$foo[1] = "b";
$foo[2] = "c";
$bar = [0, 1, 2];
@@ -368,8 +368,8 @@ function fooFoo($a) {
$c = [];
$c[$b][$b][] = "bam";',
'assertions' => [
'$a' => 'non-empty-array<string, array<int, string>>',
'$c' => 'non-empty-array<string, array<string, array<int, string>>>',
'$a' => 'array{boop:array<int, string>}',
'$c' => 'array{boop:array<string, array<int, string>>}',
],
],
'assignExplicitValueToGeneric' => [
@@ -475,7 +475,7 @@ function fooFoo($a) {
$b[$string] = 5;
$b[0] = 3;',
'assertions' => [
'$b' => 'array{0:int, c:int}',
'$b' => 'array{c:int, 0:int}',
],
],
'updateStringIntKey3' => [
@@ -276,7 +276,7 @@ function contains(array $data, array $needle): bool {
return true;
}',
'assertions' => [],
'error_levels' => ['MixedAssignment', 'MissingParamType', 'MixedArgument', 'MixedArrayOffset'],
'error_levels' => ['MixedAssignment', 'MissingParamType', 'MixedArgument', 'MixedArrayOffset', 'MixedArrayAccess'],
],
'possiblyEmptyIterable' => [
'<?php
@@ -2290,6 +2290,39 @@ public function __get(string $name) {
'$a' => 'string'
]
],
'templateAsArray' => [
'<?php
/**
* @template DATA as array<string, scalar|array|object|null>
*/
abstract class Foo {
/**
* @var DATA
*/
protected $data;
/**
* @param DATA $data
*/
public function __construct(array $data) {
$this->data = $data;
}
/**
* @return scalar|array|object|null
*/
public function __get(string $property) {
return $this->data[$property] ?? null;
}
/**
* @param scalar|array|object|null $value
*/
public function __set(string $property, $value) {
$this->data[$property] = $value;
}
}',
],
];
}

0 comments on commit 56daa39

Please sign in to comment.
You can’t perform that action at this time.