diff --git a/src/Psalm/CallMap.php b/src/Psalm/CallMap.php index c0f712253..9b169778f 100644 --- a/src/Psalm/CallMap.php +++ b/src/Psalm/CallMap.php @@ -7606,7 +7606,7 @@ return [ 'ReflectionClass::getModifiers' => ['int'], 'ReflectionClass::getNamespaceName' => ['string'], 'ReflectionClass::getName' => ['string'], -'ReflectionClass::getParentClass' => ['ReflectionClass'], +'ReflectionClass::getParentClass' => ['?ReflectionClass'], 'ReflectionClass::getProperties' => ['array', 'filter='=>'int'], 'ReflectionClass::getProperty' => ['ReflectionProperty', 'name'=>'string'], 'ReflectionClass::getShortName' => ['string'], diff --git a/src/Psalm/Checker/ClassLikeChecker.php b/src/Psalm/Checker/ClassLikeChecker.php index 251f517df..a7cb5fd26 100644 --- a/src/Psalm/Checker/ClassLikeChecker.php +++ b/src/Psalm/Checker/ClassLikeChecker.php @@ -1562,12 +1562,12 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc $class_storage = $project_checker->classlike_storage_provider->get($declaring_property_class); - $storage = $class_storage->properties[$property_name]; - - if (!$storage) { + if (!isset($class_storage->properties[$property_name])) { throw new \UnexpectedValueException('$storage should not be null for ' . $property_id); } + $storage = $class_storage->properties[$property_name]; + switch ($storage->visibility) { case self::VISIBILITY_PUBLIC: return null; diff --git a/src/Psalm/Checker/FunctionLikeChecker.php b/src/Psalm/Checker/FunctionLikeChecker.php index e6fbfb898..0dbcf342b 100644 --- a/src/Psalm/Checker/FunctionLikeChecker.php +++ b/src/Psalm/Checker/FunctionLikeChecker.php @@ -934,7 +934,7 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo } if (!$return_type) { - if ($inferred_return_type && !$inferred_return_type->isMixed()) { + if (!$inferred_return_type->isMixed()) { $this->addDocblockReturnType($project_checker, $inferred_return_type); } @@ -983,7 +983,7 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo ); } - if ($inferred_return_type && !$declared_return_type->isMixed()) { + if (!$declared_return_type->isMixed()) { if ($inferred_return_type->isVoid() && $declared_return_type->isVoid()) { return null; } diff --git a/src/Psalm/Checker/MethodChecker.php b/src/Psalm/Checker/MethodChecker.php index fdc8e0e35..8bf9959d4 100644 --- a/src/Psalm/Checker/MethodChecker.php +++ b/src/Psalm/Checker/MethodChecker.php @@ -39,9 +39,7 @@ class MethodChecker extends FunctionLikeChecker if ($method_id = self::getDeclaringMethodId($project_checker, $method_id)) { $storage = self::getStorage($project_checker, $method_id); - if ($storage) { - return $storage->params; - } + return $storage->params; } throw new \UnexpectedValueException('Cannot get method params for ' . $method_id); @@ -84,10 +82,6 @@ class MethodChecker extends FunctionLikeChecker $storage = self::getStorage($project_checker, $method_id); - if (!$storage) { - throw new \UnexpectedValueException('$storage should not be null for ' . $method_id); - } - if ($storage->return_type) { return $storage->return_type; } @@ -97,7 +91,7 @@ class MethodChecker extends FunctionLikeChecker foreach ($class_storage->overridden_method_ids[$method_name] as $overridden_method_id) { $overridden_storage = self::getStorage($project_checker, $overridden_method_id); - if ($overridden_storage && $overridden_storage->return_type) { + if ($overridden_storage->return_type) { if ($overridden_storage->return_type->isNull()) { return Type::getVoid(); } @@ -128,17 +122,13 @@ class MethodChecker extends FunctionLikeChecker $storage = self::getStorage($project_checker, $method_id); - if (!$storage) { - throw new \UnexpectedValueException('$storage should not be null for ' . $method_id); - } - if (!$storage->return_type_location) { $overridden_method_ids = self::getOverriddenMethodIds($project_checker, $method_id); foreach ($overridden_method_ids as $overridden_method_id) { $overridden_storage = self::getStorage($project_checker, $overridden_method_id); - if ($overridden_storage && $overridden_storage->return_type_location) { + if ($overridden_storage->return_type_location) { $defined_location = $overridden_storage->return_type_location; break; } @@ -262,10 +252,6 @@ class MethodChecker extends FunctionLikeChecker $storage = self::getStorage($project_checker, $method_id); - if (!$storage) { - throw new \UnexpectedValueException('$storage should not be null'); - } - if (!$storage->is_static) { if ($self_call) { if (IssueBuffer::accepts( @@ -496,10 +482,6 @@ class MethodChecker extends FunctionLikeChecker $storage = self::getStorage($project_checker, $declaring_method_id); - if (!$storage) { - throw new \UnexpectedValueException('$storage should not be null for ' . $declaring_method_id); - } - switch ($storage->visibility) { case ClassLikeChecker::VISIBILITY_PUBLIC: return true; @@ -583,10 +565,6 @@ class MethodChecker extends FunctionLikeChecker $storage = self::getStorage($project_checker, $declaring_method_id); - if (!$storage) { - throw new \UnexpectedValueException('$storage should not be null for ' . $declaring_method_id); - } - switch ($storage->visibility) { case ClassLikeChecker::VISIBILITY_PUBLIC: return null; @@ -772,10 +750,6 @@ class MethodChecker extends FunctionLikeChecker $storage = self::getStorage($project_checker, $method_id); - if (!$storage) { - throw new \UnexpectedValueException('$storage should not be null for ' . $method_id); - } - list($fq_class_name) = explode('::', $method_id); return $fq_class_name . '::' . $storage->cased_name; diff --git a/src/Psalm/Checker/ProjectChecker.php b/src/Psalm/Checker/ProjectChecker.php index 092790f7f..444889ae9 100644 --- a/src/Psalm/Checker/ProjectChecker.php +++ b/src/Psalm/Checker/ProjectChecker.php @@ -441,7 +441,6 @@ class ProjectChecker /** * @return void - * @psalm-suppress ParadoxicalCondition */ public function scanFiles() { diff --git a/src/Psalm/Checker/Statements/Expression/AssignmentChecker.php b/src/Psalm/Checker/Statements/Expression/AssignmentChecker.php index b17bdf039..7bfb42b0b 100644 --- a/src/Psalm/Checker/Statements/Expression/AssignmentChecker.php +++ b/src/Psalm/Checker/Statements/Expression/AssignmentChecker.php @@ -1146,6 +1146,7 @@ class AssignmentChecker // First go from the root element up, and go as far as we can to figure out what // array types there are while ($child_stmts) { + /** @var PhpParser\Node\Expr\ArrayDimFetch */ $child_stmt = array_shift($child_stmts); if (count($child_stmts)) { diff --git a/src/Psalm/Checker/Statements/Expression/CallChecker.php b/src/Psalm/Checker/Statements/Expression/CallChecker.php index 8d9640f82..b4c383939 100644 --- a/src/Psalm/Checker/Statements/Expression/CallChecker.php +++ b/src/Psalm/Checker/Statements/Expression/CallChecker.php @@ -171,15 +171,13 @@ class CallChecker if ($var_type_part instanceof Type\Atomic\Fn) { $function_params = $var_type_part->params; - if ($var_type_part->return_type) { - if (isset($stmt->inferredType)) { - $stmt->inferredType = Type::combineUnionTypes( - $stmt->inferredType, - $var_type_part->return_type - ); - } else { - $stmt->inferredType = $var_type_part->return_type; - } + if (isset($stmt->inferredType)) { + $stmt->inferredType = Type::combineUnionTypes( + $stmt->inferredType, + $var_type_part->return_type + ); + } else { + $stmt->inferredType = $var_type_part->return_type; } $function_exists = true; @@ -2179,7 +2177,7 @@ class CallChecker $array_arg_types[] = $array_arg_type; } - /** @var PhpParser\Node\Arg */ + /** @var ?PhpParser\Node\Arg */ $closure_arg = isset($args[$closure_index]) ? $args[$closure_index] : null; /** @var Type\Union|null */ @@ -2191,7 +2189,7 @@ class CallChecker $project_checker = $file_checker->project_checker; - if ($closure_arg_type) { + if ($closure_arg && $closure_arg_type) { $min_closure_param_count = $max_closure_param_count = count($array_arg_types); if ($method_id === 'array_filter') { diff --git a/src/Psalm/Checker/Statements/Expression/FetchChecker.php b/src/Psalm/Checker/Statements/Expression/FetchChecker.php index 2ae6cb4d5..2c84f0a56 100644 --- a/src/Psalm/Checker/Statements/Expression/FetchChecker.php +++ b/src/Psalm/Checker/Statements/Expression/FetchChecker.php @@ -850,7 +850,8 @@ class FetchChecker if (!TypeChecker::isContainedBy( $project_checker, $offset_type, - $type->type_params[0] + $type->type_params[0], + $offset_type->ignore_nullable_issues )) { $invalid_offset_types[] = (string)$type->type_params[0]; } else { @@ -939,7 +940,7 @@ class FetchChecker $type->properties[$int_key_value] ); } - } elseif ($string_key_value && $in_assignment && $string_key_value) { + } elseif ($string_key_value && $in_assignment) { $type->properties[$string_key_value] = new Type\Union([new TEmpty]); if (!$array_access_type) { @@ -968,7 +969,8 @@ class FetchChecker } elseif (TypeChecker::isContainedBy( $project_checker, $offset_type, - Type::getString() + Type::getString(), + $offset_type->ignore_nullable_issues )) { if ($replacement_type) { $generic_params = Type::combineUnionTypes( diff --git a/src/Psalm/Checker/Statements/ExpressionChecker.php b/src/Psalm/Checker/Statements/ExpressionChecker.php index d958d61a0..c4d19b8aa 100644 --- a/src/Psalm/Checker/Statements/ExpressionChecker.php +++ b/src/Psalm/Checker/Statements/ExpressionChecker.php @@ -2085,23 +2085,21 @@ class ExpressionChecker if (isset($stmt->if->inferredType)) { $lhs_type = $stmt->if->inferredType; } - } elseif ($stmt->cond) { - if (isset($stmt->cond->inferredType)) { - $if_return_type_reconciled = TypeChecker::reconcileTypes( - '!falsy', - $stmt->cond->inferredType, - '', - $statements_checker, - new CodeLocation($statements_checker->getSource(), $stmt), - $statements_checker->getSuppressedIssues() - ); + } elseif (isset($stmt->cond->inferredType)) { + $if_return_type_reconciled = TypeChecker::reconcileTypes( + '!falsy', + $stmt->cond->inferredType, + '', + $statements_checker, + new CodeLocation($statements_checker->getSource(), $stmt), + $statements_checker->getSuppressedIssues() + ); - if ($if_return_type_reconciled === false) { - return false; - } - - $lhs_type = $if_return_type_reconciled; + if ($if_return_type_reconciled === false) { + return false; } + + $lhs_type = $if_return_type_reconciled; } if (!$lhs_type || !isset($stmt->else->inferredType)) { diff --git a/src/Psalm/PropertyMap.php b/src/Psalm/PropertyMap.php index 565a225a8..b4d86aeba 100644 --- a/src/Psalm/PropertyMap.php +++ b/src/Psalm/PropertyMap.php @@ -409,6 +409,12 @@ return [ 'phpparser\\node\\expr\\new_' => [ 'args' => 'array', ], + 'phpparser\node\expr\array_' => [ + 'items' => 'array', + ], + 'phpparser\node\expr\list_' => [ + 'items' => 'array', + ], 'phpparser\\node\\expr\\methodcall' => [ 'args' => 'array', ], diff --git a/src/Psalm/Stubs/CoreGenericFunctions.php b/src/Psalm/Stubs/CoreGenericFunctions.php index c53cf63c7..62ac543b0 100644 --- a/src/Psalm/Stubs/CoreGenericFunctions.php +++ b/src/Psalm/Stubs/CoreGenericFunctions.php @@ -88,7 +88,8 @@ function array_diff_key(array $arr, array $arr2, array $arr3 = null, array $arr4 * @template TValue * * @param array $arr - * @return TValue + * @return ?TValue + * @psalm-ignore-nullable-return */ function array_shift(array &$arr) {} @@ -97,7 +98,8 @@ function array_shift(array &$arr) {} * @template TValue * * @param array $arr - * @return TValue + * @return ?TValue + * @psalm-ignore-nullable-return */ function array_pop(array &$arr) {} diff --git a/tests/ReturnTypeTest.php b/tests/ReturnTypeTest.php index 1751950e1..ff76e2dfd 100644 --- a/tests/ReturnTypeTest.php +++ b/tests/ReturnTypeTest.php @@ -518,8 +518,8 @@ class ReturnTypeTest extends TestCase ], 'mixedInferredReturnType' => [ ' 'MixedInferredReturnType', ],