From 8814dff3c1f40011dbf09573e8e1dd02b153e55e Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Mon, 20 Aug 2018 22:11:01 -0400 Subject: [PATCH] Fix #947 - interpret type hints of unpacked args --- .../Expression/AssignmentChecker.php | 27 +++++++++++++++++++ .../Checker/Statements/ExpressionChecker.php | 9 ++++--- tests/AnnotationTest.php | 18 +++++++++++++ tests/ArrayAssignmentTest.php | 9 +++++++ 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Checker/Statements/Expression/AssignmentChecker.php b/src/Psalm/Checker/Statements/Expression/AssignmentChecker.php index 572b28e62..381b44f3b 100644 --- a/src/Psalm/Checker/Statements/Expression/AssignmentChecker.php +++ b/src/Psalm/Checker/Statements/Expression/AssignmentChecker.php @@ -397,6 +397,33 @@ class AssignmentChecker ); } + foreach ($var_comments as $var_comment) { + try { + if ($var_comment->var_id === $list_var_id) { + $var_comment_type = ExpressionChecker::fleshOutType( + $statements_checker->getFileChecker()->project_checker, + $var_comment->type, + $context->self, + $context->self + ); + + $var_comment_type->setFromDocblock(); + + $new_assign_type = $var_comment_type; + break; + } + } catch (\UnexpectedValueException $e) { + if (IssueBuffer::accepts( + new InvalidDocblock( + (string)$e->getMessage(), + new CodeLocation($statements_checker->getSource(), $assign_var) + ) + )) { + // fall through + } + } + } + $context->vars_in_scope[$list_var_id] = $new_assign_type ?: Type::getMixed(); } } diff --git a/src/Psalm/Checker/Statements/ExpressionChecker.php b/src/Psalm/Checker/Statements/ExpressionChecker.php index e1dfe1cb2..a41947f23 100644 --- a/src/Psalm/Checker/Statements/ExpressionChecker.php +++ b/src/Psalm/Checker/Statements/ExpressionChecker.php @@ -47,6 +47,7 @@ use Psalm\Type\Atomic\TGenericParam; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TObject; use Psalm\Type\Atomic\TString; use Psalm\Type\TypeCombination; @@ -428,11 +429,13 @@ class ExpressionChecker foreach ($stmt->expr->inferredType->getTypes() as $type) { if ($type instanceof Scalar) { - $permissible_atomic_types[] = new TArray([Type::getInt(), new Type\Union([$type])]); + $permissible_atomic_types[] = new ObjectLike([new Type\Union([$type])]); + } elseif ($type instanceof TNull) { + $permissible_atomic_types[] = new TArray([Type::getEmpty(), Type::getEmpty()]); } elseif ($type instanceof TArray) { - $permissible_atomic_types[] = $type; + $permissible_atomic_types[] = clone $type; } elseif ($type instanceof ObjectLike) { - $permissible_atomic_types[] = $type->getGenericArrayType(); + $permissible_atomic_types[] = clone $type; } else { $all_permissible = false; break; diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index fbdb44494..3524952cd 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -1127,6 +1127,24 @@ class AnnotationTest extends TestCase bar(foo());' ], + 'listUnpackWithDocblock' => [ + 'bar();', + ], ]; } diff --git a/tests/ArrayAssignmentTest.php b/tests/ArrayAssignmentTest.php index 88977bcd2..fd01a9d35 100644 --- a/tests/ArrayAssignmentTest.php +++ b/tests/ArrayAssignmentTest.php @@ -932,6 +932,15 @@ class ArrayAssignmentTest extends TestCase $arr[BAR] = [6]; $bar = $arr[BAR][0];', ], + 'castToArray' => [ + ' "one"] : 0); + $b = (array) null;', + 'assertions' => [ + '$a' => 'array{1?:string, 0?:int}', + '$b' => 'array', + ], + ] ]; }