From 7c10a09ead7fc4a4254b49249503abb21588984e Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 8 Aug 2018 22:33:31 -0400 Subject: [PATCH] Fix #911 - properly substitute alias types Also improve handling of array_splice --- .../Statements/Expression/CallChecker.php | 80 ++++++++++++++++++- .../Checker/Statements/ReturnChecker.php | 5 +- src/Psalm/Type.php | 7 +- 3 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/Psalm/Checker/Statements/Expression/CallChecker.php b/src/Psalm/Checker/Statements/Expression/CallChecker.php index 784923c37..c70a85c7a 100644 --- a/src/Psalm/Checker/Statements/Expression/CallChecker.php +++ b/src/Psalm/Checker/Statements/Expression/CallChecker.php @@ -346,6 +346,84 @@ class CallChecker return; } + if ($method_id && $method_id === 'array_splice' && $function_params && count($args) > 1) { + $array_arg = $args[0]->value; + + if (ExpressionChecker::analyze( + $statements_checker, + $array_arg, + $context + ) === false) { + return false; + } + + $offset_arg = $args[1]->value; + + if (ExpressionChecker::analyze( + $statements_checker, + $offset_arg, + $context + ) === false) { + return false; + } + + if (!isset($args[2]) || !isset($args[3])) { + return; + } + + $length_arg = $args[2]->value; + + if (ExpressionChecker::analyze( + $statements_checker, + $length_arg, + $context + ) === false) { + return false; + } + + $replacement_arg = $args[3]->value; + + if (ExpressionChecker::analyze( + $statements_checker, + $replacement_arg, + $context + ) === false) { + return false; + } + + if (isset($array_arg->inferredType) + && $array_arg->inferredType->hasArray() + && isset($replacement_arg->inferredType) + && $replacement_arg->inferredType->hasArray() + ) { + /** @var TArray|ObjectLike */ + $array_type = $array_arg->inferredType->getTypes()['array']; + + if ($array_type instanceof ObjectLike) { + $array_type = $array_type->getGenericArrayType(); + } + + /** @var TArray|ObjectLike */ + $replacement_array_type = $replacement_arg->inferredType->getTypes()['array']; + + if ($replacement_array_type instanceof ObjectLike) { + $replacement_array_type = $replacement_array_type->getGenericArrayType(); + } + + $by_ref_type = Type\TypeCombination::combineTypes([$array_type, $replacement_array_type]); + + ExpressionChecker::assignByRefParam( + $statements_checker, + $array_arg, + $by_ref_type, + $context, + false + ); + } + + return; + } + foreach ($args as $argument_offset => $arg) { if ($function_params !== null) { $by_ref = $argument_offset < count($function_params) @@ -712,7 +790,7 @@ class CallChecker 'shuffle', 'sort', 'rsort', 'usort', 'ksort', 'asort', 'krsort', 'arsort', 'natcasesort', 'natsort', 'reset', 'end', 'next', 'prev', 'array_pop', 'array_shift', - 'array_push', 'array_unshift', 'socket_select', + 'array_push', 'array_unshift', 'socket_select', 'array_splice', ], true )) { diff --git a/src/Psalm/Checker/Statements/ReturnChecker.php b/src/Psalm/Checker/Statements/ReturnChecker.php index c97c2d5c8..9dd462312 100644 --- a/src/Psalm/Checker/Statements/ReturnChecker.php +++ b/src/Psalm/Checker/Statements/ReturnChecker.php @@ -245,8 +245,9 @@ class ReturnChecker } else { if (IssueBuffer::accepts( new InvalidReturnStatement( - 'The type \'' . $stmt->inferredType . '\' does not match the declared return ' - . 'type \'' . $local_return_type . '\' for ' . $cased_method_id, + 'The type \'' . $stmt->inferredType->getId() + . '\' does not match the declared return ' + . 'type \'' . $local_return_type->getId() . '\' for ' . $cased_method_id, new CodeLocation($source, $stmt) ), $statements_checker->getSuppressedIssues() diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index cda6a259e..71cbcd08b 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -643,11 +643,12 @@ abstract class Type if (isset($type_aliases[$string_type_token])) { $replacement_tokens = $type_aliases[$string_type_token]; + array_unshift($replacement_tokens, '('); + array_push($replacement_tokens, ')'); + $diff = count($replacement_tokens) - 1; - for ($j = 0; $j < $diff + 1; $j++) { - $type_tokens[$i + $j] = $replacement_tokens[$j]; - } + array_splice($type_tokens, $i, 1, $replacement_tokens); $i += $diff; $l += $diff;