diff --git a/docs/annotating_code/type_syntax/atomic_types.md b/docs/annotating_code/type_syntax/atomic_types.md index 5fa957be454..28d31d82918 100644 --- a/docs/annotating_code/type_syntax/atomic_types.md +++ b/docs/annotating_code/type_syntax/atomic_types.md @@ -52,19 +52,20 @@ Atomic types are the basic building block of all type information used in Psalm. - `value-of` - `T[K]` -## Top types, bottom types and empty +## Top types, bottom types ### `mixed` This is the _top type_ in PHP's type system, and represents a lack of type information. Psalm warns about `mixed` types when the `totallyTyped` flag is turned on, or when you're on level 1. ### `never` +It can be aliased to `no-return` or `never-return` in docblocks. Note: it replaced the old `empty` type that used to exist in Psalm -This is the _bottom type_ in PHP's type system, and usually represents a return type for a function that can never actually return, such as `die()`, `exit()`, or a function that always throws an exception. It may also be written in docblocks as `no-return` or `never-return`. - -### `empty` - -A type that's equivalent to a "coming soon" sign. Psalm uses this type when it’s awaiting more information — a good example is the type of the empty array `[]`, which Psalm types as `array`. Psalm treats `empty` in a somewhat similar fashion to `never` when combining types together — `empty|int` becomes `int`, just as `never|string` becomes `string`. +This is the _bottom type_ in PHP's type system. It's used to describe a type that has no possible value. It can happen in multiple cases: +- the actual `never` type from PHP 8.1 (can be used in docblocks for older versions). This type can be used as a return type for functions that will never return, either because they always throw exceptions or always exit() +- an union type that have been stripped for all its possible types. (For example, if a variable is `string|int` and we perform a is_bool() check in a condition, the type of the variable in the condition will be `never` as the condition will never be entered) +- it can represent a placeholder for types yet to come — a good example is the type of the empty array `[]`, which Psalm types as `array`, the content of the array is void so it can accept any content +- it can also happen in the same context as the line above for templates that have yet to be defined ## Other diff --git a/docs/running_psalm/plugins/plugins_type_system.md b/docs/running_psalm/plugins/plugins_type_system.md index 644cc0c3e45..311bf37df56 100644 --- a/docs/running_psalm/plugins/plugins_type_system.md +++ b/docs/running_psalm/plugins/plugins_type_system.md @@ -27,7 +27,7 @@ The classes are as follows: `TNull` - denotes the `null` type -`TNever` - denotes the `no-return`/`never-return` type for functions that never return, either throwing an exception or terminating (like the builtin `exit()`). +`TNever` - denotes the `no-return`/`never-return` type for functions that never return, either throwing an exception or terminating (like the builtin `exit()`). Also used for union types that can have no possible types (impossible intersections for example). Empty arrays `[]` have the type `array`. `TMixed` - denotes the `mixed` type, used when you don’t know the type of an expression. @@ -35,8 +35,6 @@ The classes are as follows: `TEmptyMixed` - as above, but empty. Generated for `$x` inside the `if` statement `if (!$x) {...}` when `$x` is `mixed` outside. -`TEmpty` - denotes the `empty` type, used to describe a type corresponding to no value whatsoever. Empty arrays `[]` have the type `array`. - `TIterable` - denotes the [`iterable` type](https://www.php.net/manual/en/language.types.iterable.php) (which can also result from an `is_iterable` check). `TResource` - denotes the `resource` type (e.g. a file handle). diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index b4800c82f69..97ec0ed11f5 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1553,7 +1553,7 @@ private function checkForMissingPropertyType( if ($suggested_type && !$suggested_type->isNull()) { $message .= ' - consider ' . str_replace( - ['', ''], + ['', ''], '', (string)$suggested_type ); diff --git a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php index 0b4934e8de0..c7d608722a8 100644 --- a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php @@ -50,7 +50,7 @@ abstract class ClassLikeAnalyzer extends SourceAnalyzer 'bool' => 'bool', 'false' => 'false', 'object' => 'object', - 'empty' => 'empty', + 'never' => 'never', 'callable' => 'callable', 'array' => 'array', 'iterable' => 'iterable', diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php index e8722b1bfdc..c1dd9f1c2d1 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php @@ -237,12 +237,12 @@ public static function verifyReturnType( } $number_of_types = count($inferred_return_type_parts); - // we filter TNever and TEmpty that have no bearing on the return type + // we filter TNever that have no bearing on the return type if ($number_of_types > 1) { $inferred_return_type_parts = array_filter( $inferred_return_type_parts, static function (Union $union_type): bool { - return !($union_type->isNever() || $union_type->isEmpty()); + return !$union_type->isNever(); } ); } @@ -490,7 +490,7 @@ static function (Union $union_type): bool { return null; } - if ($inferred_return_type->hasMixed() || $inferred_return_type->isEmpty()) { + if ($inferred_return_type->hasMixed()) { if (IssueBuffer::accepts( new MixedInferredReturnType( 'Could not verify return type \'' . $declared_return_type . '\' for ' . diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 11beca2d23d..9c6c841bdd8 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -503,7 +503,7 @@ public function analyze( && !$inferred_return_type->isSingleIntLiteral() && !$inferred_return_type->isSingleStringLiteral() && !$inferred_return_type->isTrue() - && $inferred_return_type->getId() !== 'array' + && $inferred_return_type->getId() !== 'array' ) { $manipulator->makePure(); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index bdcf834154e..852abdc8f62 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -49,6 +49,7 @@ use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNull; @@ -434,9 +435,7 @@ public static function checkIteratorType( } // if it's an empty array, we cannot iterate over it - if ($iterator_atomic_type instanceof TArray - && $iterator_atomic_type->type_params[1]->isEmpty() - ) { + if ($iterator_atomic_type instanceof TArray && $iterator_atomic_type->isEmptyArray()) { $always_non_empty_array = false; $has_valid_iterator = true; continue; @@ -505,7 +504,10 @@ public static function checkIteratorType( $invalid_iterator_types[] = $iterator_atomic_type->getKey(); $value_type = Type::getMixed(); - } elseif ($iterator_atomic_type instanceof TObject || $iterator_atomic_type instanceof TMixed) { + } elseif ($iterator_atomic_type instanceof TObject || + $iterator_atomic_type instanceof TMixed || + $iterator_atomic_type instanceof TNever + ) { $has_valid_iterator = true; $value_type = Type::getMixed(); $key_type = Type::getMixed(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php index fd492eba78d..c6e79105081 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php @@ -171,7 +171,7 @@ public static function analyze( ); if (isset($case_vars_in_scope_reconciled[$switch_var_id]) - && $case_vars_in_scope_reconciled[$switch_var_id]->isEmpty() + && $case_vars_in_scope_reconciled[$switch_var_id]->isNever() ) { $all_options_matched = true; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php index d8802a1cd47..3e97c915ba0 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php @@ -591,7 +591,7 @@ private static function handleNonReturningCase( if (!$case->cond && $switch_var_id && isset($case_context->vars_in_scope[$switch_var_id]) - && $case_context->vars_in_scope[$switch_var_id]->isEmpty() + && $case_context->vars_in_scope[$switch_var_id]->isNever() ) { if (IssueBuffer::accepts( new ParadoxicalCondition( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index d5df517ce26..98dc159f6fe 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -547,7 +547,7 @@ private static function handleUnpackedArray( && count($unpacked_atomic_type->type_params) === 2 )) { /** @psalm-suppress PossiblyUndefinedArrayOffset provably true, but Psalm can’t see it */ - if ($unpacked_atomic_type->type_params[1]->isEmpty()) { + if ($unpacked_atomic_type->type_params[1]->isNever()) { continue; } $array_creation_info->can_create_objectlike = false; @@ -579,7 +579,7 @@ private static function handleUnpackedArray( ) ); } elseif ($unpacked_atomic_type instanceof TList) { - if ($unpacked_atomic_type->type_param->isEmpty()) { + if ($unpacked_atomic_type->type_param->isNever()) { continue; } $array_creation_info->can_create_objectlike = false; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php index a2eb2c60f0a..7652589699b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php @@ -739,7 +739,7 @@ private static function analyzeNestedArrayAssignment( return; } - if ($child_stmt_var_type->isEmpty()) { + if ($child_stmt_var_type->isNever()) { $child_stmt_var_type = Type::getEmptyArray(); $statements_analyzer->node_data->setType($child_stmt->var, $child_stmt_var_type); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 407c6d0441e..9f9814669fc 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -569,7 +569,7 @@ public static function analyze( return false; } - $context->vars_in_scope[$var_id] = Type::getEmpty(); + $context->vars_in_scope[$var_id] = Type::getNever(); $context->inside_assignment = $was_in_assignment; @@ -1024,11 +1024,11 @@ public static function assignByRefParam( $by_ref_out_type->parent_nodes += $existing_type->parent_nodes; } - if ($existing_type->getId() !== 'array') { + if ($existing_type->getId() !== 'array') { $context->vars_in_scope[$var_id] = $by_ref_out_type; if (!($stmt_type = $statements_analyzer->node_data->getType($stmt)) - || $stmt_type->isEmpty() + || $stmt_type->isNever() ) { $statements_analyzer->node_data->setType($stmt, clone $by_ref_type); } @@ -1043,7 +1043,7 @@ public static function assignByRefParam( $stmt_type = $statements_analyzer->node_data->getType($stmt); - if (!$stmt_type || $stmt_type->isEmpty()) { + if (!$stmt_type || $stmt_type->isNever()) { $statements_analyzer->node_data->setType($stmt, clone $by_ref_type); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php index f7f7752c1ef..037f426ec20 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php @@ -75,9 +75,9 @@ public static function analyze( $right_type = $nodes->getType($right); $config = Config::getInstance(); - if ($left_type && $left_type->isEmpty()) { + if ($left_type && $left_type->isNever()) { $left_type = $right_type; - } elseif ($right_type && $right_type->isEmpty()) { + } elseif ($right_type && $right_type->isNever()) { $right_type = $left_type; } @@ -896,7 +896,7 @@ public static function arithmeticOperation( $result = $operand1 - $operand2; } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Mod) { if ($operand2 === 0) { - return Type::getEmpty(); + return Type::getNever(); } $result = $operand1 % $operand2; @@ -916,7 +916,7 @@ public static function arithmeticOperation( $result = $operand1 >> $operand2; } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Div) { if ($operand2 === 0) { - return Type::getEmpty(); + return Type::getNever(); } $result = $operand1 / $operand2; @@ -1269,9 +1269,11 @@ private static function analyzePowBetweenIntRange( $new_result_type = Type::getInt(true, 0); } elseif ($right_type_part->min_bound === 0 && $right_type_part->max_bound === 0) { $new_result_type = Type::getInt(true, 1); + } elseif ($right_type_part->isNegative()) { + $new_result_type = Type::getFloat(); } else { - //technically could be a float(INF)... - $new_result_type = Type::getEmpty(); + $new_result_type = new Union([new TFloat(), new TLiteralInt(0), new TLiteralInt(1)]); + $new_result_type->from_calculation = true; } } else { //$left_type_part may be a mix of positive, negative and 0 @@ -1305,7 +1307,7 @@ private static function analyzeModBetweenIntRange( if ($right_type_part->min_bound !== null && $right_type_part->min_bound === $right_type_part->max_bound) { //if the second operand is a literal, we can be pretty detailed if ($right_type_part->max_bound === 0) { - $new_result_type = Type::getEmpty(); + $new_result_type = Type::getNever(); } else { if ($left_type_part->isPositiveOrZero()) { if ($right_type_part->isPositive()) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 4a4ac8c3afb..b309443e907 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -1360,7 +1360,7 @@ private static function coerceValueAfterGatekeeperArgument( && $input_atomic_type->value === $param_atomic_type->value ) { foreach ($input_atomic_type->type_params as $i => $type_param) { - if ($type_param->isEmpty() && isset($param_atomic_type->type_params[$i])) { + if ($type_param->isNever() && isset($param_atomic_type->type_params[$i])) { $input_type_changed = true; $input_atomic_type->type_params[$i] = clone $param_atomic_type->type_params[$i]; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php index 1404cb97a45..a585baf22f3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php @@ -1207,7 +1207,7 @@ private static function evaluateArbitraryParam( ); foreach ($context->vars_in_scope[$var_id]->getAtomicTypes() as $type) { - if ($type instanceof TArray && $type->type_params[1]->isEmpty()) { + if ($type instanceof TArray && $type->isEmptyArray()) { $context->vars_in_scope[$var_id]->removeType('array'); $context->vars_in_scope[$var_id]->addType( new TArray( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php index 654e9bd829b..656e64dcbef 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php @@ -33,9 +33,9 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TCallable; use Psalm\Type\Atomic\TClosure; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Union; @@ -544,8 +544,8 @@ public static function handleByRefArrayAdjustment( if ($array_atomic_type->count === 0) { $array_atomic_type = new TArray( [ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever]), + new Union([new TNever]), ] ); } else { @@ -561,8 +561,8 @@ public static function handleByRefArrayAdjustment( if ($array_atomic_type->count === 0) { $array_atomic_type = new TArray( [ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever]), + new Union([new TNever]), ] ); } else { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php index af190ce1d20..94005bf8f8f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php @@ -144,7 +144,7 @@ public static function fetch( $template_result->lower_bounds[$template_name] = [ 'fn-' . $function_id => [ new TemplateBound( - Type::getEmpty() + Type::getNever() ) ] ]; @@ -390,14 +390,13 @@ private static function getReturnTypeFromCallMapWithArgs( foreach ($atomic_types['array']->properties as $property) { // empty, never and possibly undefined can't count for min value if (!$property->possibly_undefined - && !$property->isEmpty() && !$property->isNever() ) { $min++; } - //empty and never can't count for max value because we know keys are undefined - if (!$property->isEmpty() && !$property->isNever()) { + //never can't count for max value because we know keys are undefined + if (!$property->isNever()) { $max++; } } @@ -416,8 +415,7 @@ private static function getReturnTypeFromCallMapWithArgs( } if ($atomic_types['array'] instanceof TArray - && $atomic_types['array']->type_params[0]->isEmpty() - && $atomic_types['array']->type_params[1]->isEmpty() + && $atomic_types['array']->isEmptyArray() ) { return Type::getInt(false, 0); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php index 33d6d173ac6..7231b944ea7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php @@ -27,12 +27,12 @@ use Psalm\Storage\ClassLikeStorage; use Psalm\Type; use Psalm\Type\Atomic; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyMixed; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TObject; @@ -571,7 +571,7 @@ private static function handleInvalidClass( case TTemplateParam::class: case TEmptyMixed::class: - case TEmpty::class: + case TNever::class: case TMixed::class: case TNonEmptyMixed::class: case TObject::class: diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php index 44bd32a59b3..09fe3f8ff6a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php @@ -588,7 +588,7 @@ public static function replaceTemplateTypes( } else { $template_result->lower_bounds[$template_type->param_name] = [ ($template_type->defining_class) => [ - new TemplateBound(Type::getEmpty()) + new TemplateBound(Type::getNever()) ] ]; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index 2f9548c4195..90ec3879495 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -516,7 +516,7 @@ function ($bounds) use ($codebase) { ); } else { if ($fq_class_name === 'SplObjectStorage') { - $generic_param_type = Type::getEmpty(); + $generic_param_type = Type::getNever(); } else { $generic_param_type = clone array_values($base_type)[0]; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php index 97435117100..04767d9ff6a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php @@ -522,7 +522,7 @@ private static function getMethodReturnType( } else { $template_result->lower_bounds[$template_type->param_name] = [ ($template_type->defining_class) => [ - new TemplateBound(Type::getEmpty()) + new TemplateBound(Type::getNever()) ] ]; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index 6aee61d6dc9..ac5cc2fdaa5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -232,7 +232,7 @@ public static function analyze( $keyed_array->is_list = true; $permissible_atomic_types[] = $keyed_array; } elseif ($type instanceof TNull) { - $permissible_atomic_types[] = new TArray([Type::getEmpty(), Type::getEmpty()]); + $permissible_atomic_types[] = new TArray([Type::getNever(), Type::getNever()]); } elseif ($type instanceof TArray || $type instanceof TList || $type instanceof TKeyedArray diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php index 308a5e77aff..be1b835ff95 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php @@ -136,7 +136,7 @@ public static function analyze( } } - $statements_analyzer->node_data->setType($stmt, Type::getEmpty()); + $statements_analyzer->node_data->setType($stmt, Type::getNever()); $context->has_returned = true; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index 7a7d167a84c..09a30c4262b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -56,7 +56,6 @@ use Psalm\Type\Atomic\TClassConstant; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClassStringMap; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; @@ -67,6 +66,7 @@ use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNull; @@ -553,7 +553,10 @@ public static function getArrayAccessTypeGivenOffset( foreach ($array_type->getAtomicTypes() as $type_string => $type) { $original_type = $type; - if ($type instanceof TMixed || $type instanceof TTemplateParam || $type instanceof TEmpty) { + if ($type instanceof TMixed + || $type instanceof TTemplateParam + || $type instanceof TNever + ) { if (!$type instanceof TTemplateParam || $type->as->isMixed() || !$type->as->isSingle()) { $array_access_type = self::handleMixedArrayAccess( $context, @@ -592,7 +595,7 @@ public static function getArrayAccessTypeGivenOffset( $statements_analyzer->getSuppressedIssues() ); - $array_access_type = new Union([new TEmpty]); + $array_access_type = new Union([new TNever]); } } else { if (!$context->inside_isset && !MethodCallAnalyzer::hasNullsafe($stmt->var)) { @@ -1002,7 +1005,7 @@ public static function replaceOffsetTypeWithInts(Union $offset_type): Union } /** - * @param TMixed|TTemplateParam|TEmpty $type + * @param TMixed|TTemplateParam|TNever $type */ public static function handleMixedArrayAccess( Context $context, @@ -1073,7 +1076,7 @@ public static function handleMixedArrayAccess( return Type::combineUnionTypes( $array_access_type, - Type::getMixed($type instanceof TEmpty) + Type::getMixed($type instanceof TNever) ); } @@ -1105,10 +1108,9 @@ private static function handleArrayAccessOnArray( if ($in_assignment && $type instanceof TArray - && $type->type_params[0]->isEmpty() - && $type->type_params[1]->isEmpty() + && $type->isEmptyArray() ) { - $from_empty_array = $type->type_params[0]->isEmpty() && $type->type_params[1]->isEmpty(); + $from_empty_array = $type->isEmptyArray(); if (count($key_values) === 1) { $from_mixed_array = $type->type_params[1]->isMixed(); @@ -1118,7 +1120,7 @@ private static function handleArrayAccessOnArray( // ok, type becomes an TKeyedArray $array_type->removeType($type_string); $type = new TKeyedArray([ - $key_values[0] => $from_mixed_array ? Type::getMixed() : Type::getEmpty() + $key_values[0] => $from_mixed_array ? Type::getMixed() : Type::getNever() ]); $type->sealed = $from_empty_array; @@ -1240,12 +1242,12 @@ private static function handleArrayAccessOnTArray( ): void { // if we're assigning to an empty array with a key offset, refashion that array if ($in_assignment) { - if ($type->type_params[0]->isEmpty()) { + if ($type->isEmptyArray()) { $type->type_params[0] = $offset_type->isMixed() ? Type::getArrayKey() : $offset_type; } - } elseif (!$type->type_params[0]->isEmpty()) { + } elseif (!$type->isEmptyArray()) { $expected_offset_type = $type->type_params[0]->hasMixed() ? new Union([new TArrayKey]) : $type->type_params[0]; @@ -1366,7 +1368,7 @@ private static function handleArrayAccessOnTArray( $type->type_params[1] ); - if ($array_access_type->isEmpty() + if ($array_access_type->isNever() && !$array_type->hasMixed() && !$in_assignment && !$context->inside_isset @@ -1525,7 +1527,7 @@ private static function handleArrayAccessOnKeyedArray( clone $type->properties[$key_value] ); } elseif ($in_assignment) { - $type->properties[$key_value] = new Union([new TEmpty]); + $type->properties[$key_value] = new Union([new TNever]); $array_access_type = Type::combineUnionTypes( $array_access_type, @@ -1948,7 +1950,7 @@ private static function handleArrayAccessOnString( $valid_offset_type = Type::getInt(false, 0); } elseif ($type instanceof TLiteralString) { if ($type->value === '') { - $valid_offset_type = Type::getEmpty(); + $valid_offset_type = Type::getNever(); } elseif (strlen($type->value) < 10) { $valid_offsets = []; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php index 35fb44b1acd..775c86ceaf9 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php @@ -120,7 +120,7 @@ public static function analyze( return true; } - if ($stmt_var_type->isEmpty()) { + if ($stmt_var_type->isNever()) { if (IssueBuffer::accepts( new MixedPropertyFetch( 'Cannot fetch property on empty var ' . $stmt_var_id, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php index 5a1bb9975ed..0db311700b3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php @@ -697,7 +697,7 @@ private static function handleUnpackedArray( $array_creation_info->property_types[$new_offset] = $property_value; } } elseif ($unpacked_atomic_type instanceof TArray) { - if ($unpacked_atomic_type->type_params[1]->isEmpty()) { + if ($unpacked_atomic_type->isEmptyArray()) { continue; } $array_creation_info->can_create_objectlike = false; @@ -719,7 +719,7 @@ private static function handleUnpackedArray( ) ); } elseif ($unpacked_atomic_type instanceof TList) { - if ($unpacked_atomic_type->type_param->isEmpty()) { + if ($unpacked_atomic_type->type_param->isNever()) { continue; } $array_creation_info->can_create_objectlike = false; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php index d539c3ef467..98b7cc8a542 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php @@ -138,7 +138,7 @@ public static function analyze( $expression_type = Type::getMixed(); } } else { - $expression_type = Type::getEmpty(); + $expression_type = Type::getNever(); } $yield_type = null; diff --git a/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php index 6936fc33e14..64fe69b8935 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php @@ -178,7 +178,7 @@ public static function analyze( $statements_analyzer->getSuppressedIssues() ); - $stmt_type = Type::getEmpty(); + $stmt_type = Type::getNever(); } if ($stmt_type->isVoid()) { diff --git a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php index 665a1c40d4d..fbe259dfeff 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php @@ -85,7 +85,7 @@ public static function analyze( } if ($stmt instanceof PhpParser\Node\Expr\Throw_) { - $statements_analyzer->node_data->setType($stmt, Type::getEmpty()); + $statements_analyzer->node_data->setType($stmt, Type::getNever()); } return true; diff --git a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php index c7cfeb6d5a3..47da7c91362 100644 --- a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php @@ -9,10 +9,10 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyMixed; use Psalm\Type\Union; @@ -84,8 +84,8 @@ public static function analyze( } else { $root_type->addType( new TArray([ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever]), + new Union([new TNever]), ]) ); } diff --git a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php index c27cc523f7c..dfc1a7f0d4e 100644 --- a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php +++ b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php @@ -25,7 +25,6 @@ use Psalm\Type; use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArray; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TLiteralClassString; @@ -33,6 +32,7 @@ use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TString; use Psalm\Type\Atomic\TTrue; @@ -185,7 +185,7 @@ public static function resolve( $auto_key = 0; if (!$c->entries) { - return new TArray([Type::getEmpty(), Type::getEmpty()]); + return new TArray([Type::getNever(), Type::getNever()]); } $is_list = true; @@ -199,7 +199,7 @@ public static function resolve( $visited_constant_ids + [$c_id => true] ); - if ($spread_array instanceof TArray && $spread_array->type_params[1]->isEmpty()) { + if ($spread_array instanceof TArray && $spread_array->isEmptyArray()) { continue; } @@ -255,8 +255,8 @@ public static function resolve( if (empty($properties)) { $resolved_type = new TArray([ - new Union([new TEmpty()]), - new Union([new TEmpty()]), + new Union([new TNever()]), + new Union([new TNever()]), ]); } else { $resolved_type = new TKeyedArray($properties); diff --git a/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php b/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php index 147ba4e7ccf..3e8bbe7ae6d 100644 --- a/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php +++ b/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php @@ -300,7 +300,7 @@ public function setParamType( string $new_type, string $phpdoc_type ): void { - $new_type = str_replace(['', '', ''], '', $new_type); + $new_type = str_replace(['', '', ''], '', $new_type); if ($php_type === 'static') { $php_type = ''; diff --git a/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php b/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php index 0ee9654a462..a8d560758b8 100644 --- a/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php +++ b/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php @@ -154,7 +154,7 @@ public function setType( bool $is_php_compatible, ?string $description = null ): void { - $new_type = str_replace(['', '', ''], '', $new_type); + $new_type = str_replace(['', '', ''], '', $new_type); $this->new_php_type = $php_type; $this->new_phpdoc_type = $phpdoc_type; diff --git a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php index fecbc070cb0..ccd38f4243f 100644 --- a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php +++ b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php @@ -262,7 +262,7 @@ public function hover(TextDocumentIdentifier $textDocument, Position $position): * * @param TextDocumentIdentifier $textDocument The text document * @param Position $position The position - * @psalm-return Promise>|Promise + * @psalm-return Promise>|Promise */ public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise { diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php index 6cc324213dd..fa11cf9f74d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php @@ -109,7 +109,7 @@ static function ($keyed_type) use ($statements_source, $context) { $first_arg_array->properties ), static function ($keyed_type) { - return !$keyed_type->isEmpty(); + return !$keyed_type->isNever(); } ); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index 578560b635c..1e038602bc9 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -154,7 +154,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } } } else { - if (!$unpacked_type_part->type_params[0]->isEmpty()) { + if (!$unpacked_type_part->isEmptyArray()) { foreach ($generic_properties as $key => $keyed_type) { $generic_properties[$key] = Type::combineUnionTypes( $keyed_type, @@ -169,7 +169,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } if ($unpacked_type_part instanceof TArray) { - if ($unpacked_type_part->type_params[1]->isEmpty()) { + if ($unpacked_type_part->isEmptyArray()) { continue; } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php index 66d39525603..d0563dbaaca 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php @@ -80,7 +80,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev throw new UnexpectedValueException('This should never happen'); } - if ($value_type->isEmpty()) { + if ($value_type->isNever()) { $value_type = Type::getFalse(); } elseif (($function_id !== 'reset' && $function_id !== 'end') || !$definitely_has_items) { $value_type->addType(new TFalse); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php index 25dd2bd2ccc..594918e2b37 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php @@ -55,7 +55,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($first_arg_array instanceof TArray) { $value_type = clone $first_arg_array->type_params[1]; - if ($value_type->isEmpty()) { + if ($first_arg_array->isEmptyArray()) { return Type::getNull(); } diff --git a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php index 19c6266bbca..224b0e5a523 100644 --- a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php @@ -27,7 +27,6 @@ use Psalm\Type\Atomic\TDependentGetDebugType; use Psalm\Type\Atomic\TDependentGetType; use Psalm\Type\Atomic\TDependentListKey; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEmptyNumeric; use Psalm\Type\Atomic\TEmptyScalar; diff --git a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php index d1b31b89578..a090faee460 100644 --- a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php @@ -7,11 +7,11 @@ use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TClassStringMap; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Union; @@ -39,8 +39,8 @@ public static function isContainedBy( $is_empty_array = $input_type_part->equals( new TArray([ - new Union([new TEmpty()]), - new Union([new TEmpty()]) + new Union([new TNever()]), + new Union([new TNever()]) ]), false ); @@ -102,7 +102,7 @@ public static function isContainedBy( if ($container_type_part instanceof TList && $input_type_part instanceof TArray - && $input_type_part->type_params[1]->isEmpty() + && $input_type_part->isEmptyArray() ) { return !$container_type_part instanceof TNonEmptyList; } @@ -225,7 +225,7 @@ function ($i) { continue; } - if ($input_param->isEmpty() + if ($input_param->isNever() && $container_type_part instanceof TNonEmptyArray ) { return false; @@ -233,7 +233,7 @@ function ($i) { $param_comparison_result = new TypeComparisonResult(); - if (!$input_param->isEmpty()) { + if (!$input_param->isNever()) { if (!UnionTypeComparator::isContainedBy( $codebase, $input_param, diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index b6045f4df91..ed209cacdf5 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -15,7 +15,6 @@ use Psalm\Type\Atomic\TClassStringMap; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TConditional; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEnumCase; use Psalm\Type\Atomic\TGenericObject; @@ -94,7 +93,7 @@ public static function isContainedBy( return true; } - if ($input_type_part instanceof TNever || $input_type_part instanceof TEmpty) { + if ($input_type_part instanceof TNever) { return true; } @@ -472,7 +471,7 @@ public static function isContainedBy( $array_comparison_result = new TypeComparisonResult(); - if (!$input_param->isEmpty()) { + if (!$input_param->isNever()) { if (!UnionTypeComparator::isContainedBy( $codebase, $input_param, diff --git a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php index dd7e648c3fd..c63f56dd8c2 100644 --- a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php @@ -94,7 +94,7 @@ public static function isContainedBy( $container_param = $container_type_part->type_params[$i]; - if ($input_param->isEmpty()) { + if ($input_param->isNever()) { if ($atomic_comparison_result) { if (!$atomic_comparison_result->replacement_atomic_type) { $atomic_comparison_result->replacement_atomic_type = clone $input_type_part; diff --git a/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php b/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php index bb42838b7e8..7c372e438b8 100644 --- a/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php +++ b/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php @@ -42,7 +42,7 @@ public static function isContainedBy( $property_type_comparison = new TypeComparisonResult(); - if (!$input_property_type->isEmpty()) { + if (!$input_property_type->isNever()) { if (!UnionTypeComparator::isContainedBy( $codebase, $input_property_type, @@ -127,7 +127,7 @@ public static function isContainedByObjectWithProperties( $property_type_comparison = new TypeComparisonResult(); - if (!$input_property_type->isEmpty() + if (!$input_property_type->isNever() && !UnionTypeComparator::isContainedBy( $codebase, $input_property_type, diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index f25a516018e..ef19ad510d3 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -77,7 +77,6 @@ public static function reconcile( return $existing_var_type; } - $existing_var_atomic_types = $existing_var_type->getAtomicTypes(); if ($assertion === 'false' && isset($existing_var_atomic_types['bool'])) { diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index 2c6562fc221..c943f623581 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -19,7 +19,6 @@ use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TCallableString; use Psalm\Type\Atomic\TClassString; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEmptyNumeric; use Psalm\Type\Atomic\TEmptyScalar; @@ -35,6 +34,7 @@ use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNonEmptyLowercaseString; @@ -508,13 +508,15 @@ private static function reconcileIsset( if (empty($existing_var_type->getAtomicTypes())) { $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; - return Type::getEmpty(); + return Type::getNever(); } } - if ($existing_var_type->hasType('empty')) { - $existing_var_type->removeType('empty'); - $existing_var_type->addType(new TMixed($inside_loop)); + if ($inside_loop) { + if ($existing_var_type->hasType('never')) { + $existing_var_type->removeType('never'); + $existing_var_type->addType(new TMixed(true)); + } } $existing_var_type->from_property = false; @@ -550,7 +552,7 @@ private static function reconcileNonEmptyCountable( if (!$array_atomic_type instanceof TNonEmptyArray || ($array_atomic_type->count < $min_count) ) { - if ($array_atomic_type->getId() === 'array') { + if ($array_atomic_type->getId() === 'array') { $existing_var_type->removeType('array'); } else { $non_empty_array = new TNonEmptyArray( @@ -723,7 +725,7 @@ private static function reconcilePositiveNumeric( $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; - return Type::getEmpty(); + return Type::getNever(); } /** @@ -922,7 +924,7 @@ private static function reconcileString( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1020,7 +1022,7 @@ private static function reconcileInt( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1099,7 +1101,7 @@ private static function reconcileBool( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1174,7 +1176,7 @@ private static function reconcileScalar( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1266,7 +1268,7 @@ private static function reconcileNumeric( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1359,7 +1361,7 @@ private static function reconcileObject( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1416,7 +1418,7 @@ private static function reconcileResource( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1777,7 +1779,7 @@ private static function reconcileTraversable( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1872,7 +1874,7 @@ private static function reconcileArray( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -1925,9 +1927,7 @@ private static function reconcileList( } } - if ($type->type_params[0]->isEmpty() - || $type->type_params[1]->isEmpty() - ) { + if ($type->isEmptyArray()) { //we allow an empty array to pass as a list. We keep the type as empty array though (more precise) $array_types[] = $type; } @@ -1977,7 +1977,7 @@ private static function reconcileList( return $existing_var_type->from_docblock ? Type::getMixed() - : Type::getEmpty(); + : Type::getNever(); } /** @@ -2265,7 +2265,7 @@ private static function reconcileFalsyOrEmpty( $failed_reconciliation = 2; - return Type::getEmpty(); + return Type::getNever(); } if (!$did_remove_type) { @@ -2297,8 +2297,8 @@ private static function reconcileFalsyOrEmpty( $existing_var_type->removeType('array'); $existing_var_type->addType(new TArray( [ - new Union([new TEmpty()]), - new Union([new TEmpty()]), + new Union([new TNever()]), + new Union([new TNever()]), ] )); } diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index f3d8591f53f..f86dfeaf466 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -18,7 +18,6 @@ use Psalm\Type\Atomic\TCallableArray; use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TCallableString; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TInt; @@ -31,6 +30,7 @@ use Psalm\Type\Atomic\TLowercaseString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNonEmptyLowercaseString; @@ -75,7 +75,7 @@ public static function reconcile( ): ?Union { if ($assertion === 'isset') { if ($existing_var_type->possibly_undefined) { - return Type::getEmpty(); + return Type::getNever(); } if (!$existing_var_type->isNullable() @@ -128,14 +128,14 @@ public static function reconcile( return $existing_var_type->from_docblock ? Type::getNull() - : Type::getEmpty(); + : Type::getNever(); } return Type::getNull(); } if ($assertion === 'array-key-exists') { - return Type::getEmpty(); + return Type::getNever(); } if (strpos($assertion, 'in-array-') === 0) { @@ -483,14 +483,14 @@ private static function reconcileNonEmptyCountable( $did_remove_type = true; $existing_var_type->removeType('array'); - } elseif ($array_atomic_type->getId() !== 'array') { + } elseif ($array_atomic_type->getId() !== 'array') { $did_remove_type = true; if (!$min_count) { $existing_var_type->addType(new TArray( [ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever()]), + new Union([new TNever()]), ] )); } @@ -714,7 +714,7 @@ private static function reconcileFalsyOrEmpty( $failed_reconciliation = 2; - return Type::getEmpty(); + return Type::getNever(); } if (!$did_remove_type) { diff --git a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php index 1dc6033b60e..af4ad25914a 100644 --- a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php +++ b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php @@ -1099,7 +1099,7 @@ function (TemplateBound $bound_a, TemplateBound $bound_b) { if ($current_depth === null) { $current_depth = $template_bound->appearance_depth; } elseif ($current_depth !== $template_bound->appearance_depth && $current_type) { - if (!$current_type->isEmpty() + if (!$current_type->isNever() && ($had_invariant || $last_arg_offset === $template_bound->arg_offset) ) { // escape switches when matching on invariant generic params diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index 99fbeffff30..330ce7b8f7e 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -17,7 +17,6 @@ use Psalm\Type\Atomic\TCallableString; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClassStringMap; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; @@ -78,8 +77,8 @@ class TypeCombiner * - so `int + string = int|string` * - so `array + array = array` * - and `array + string = array|string` - * - and `array + array = array` - * - and `array + array = array` + * - and `array + array = array` + * - and `array + array = array` * - and `array + array = array` * * @param non-empty-list $types @@ -336,20 +335,17 @@ public static function combine( $combination->value_types += $combination->named_object_types; } - $has_empty = (int) isset($combination->value_types['empty']); - $has_never = false; + $has_never = isset($combination->value_types['never']); foreach ($combination->value_types as $type) { if ($type instanceof TMixed && $combination->mixed_from_loop_isset - && (count($combination->value_types) > (1 + $has_empty) || count($new_types) > $has_empty) + && (count($combination->value_types) > (1 + (int) $has_never) || count($new_types) > (int) $has_never) ) { continue; } - if (($type instanceof TEmpty || $type instanceof TNever) - && (count($combination->value_types) > 1 || count($new_types)) - ) { + if ($type instanceof TNever && (count($combination->value_types) > 1 || count($new_types))) { $has_never = true; continue; } @@ -552,7 +548,7 @@ private static function scrapeTypeProperties( $combination->array_always_filled = false; } - if (!$type->type_params[1]->isEmpty()) { + if (!$type->isEmptyArray()) { $combination->all_arrays_lists = false; $combination->all_arrays_class_string_maps = false; } @@ -1330,11 +1326,11 @@ private static function handleKeyedArrayEntries( } if (!$combination->array_type_params - || $combination->array_type_params[1]->isEmpty() + || $combination->array_type_params[1]->isNever() ) { if (!$overwrite_empty_array && ($combination->array_type_params - && ($combination->array_type_params[1]->isEmpty() + && ($combination->array_type_params[1]->isNever() || $combination->array_type_params[1]->isMixed())) ) { foreach ($combination->objectlike_entries as $objectlike_entry) { diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index 3709534e47e..21875186c39 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -14,7 +14,6 @@ use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TConditional; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntMask; @@ -827,13 +826,12 @@ private static function expandConditional( ); $number_of_types = count($all_conditional_return_types); - // we filter TNever and TEmpty that have no bearing on the return type + // we filter TNever that have no bearing on the return type if ($number_of_types > 1) { $all_conditional_return_types = array_filter( $all_conditional_return_types, static function (Atomic $atomic_type): bool { - return !($atomic_type instanceof TEmpty - || $atomic_type instanceof TNever); + return !$atomic_type instanceof TNever; } ); } diff --git a/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php b/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php index e7d3f066031..7c619489711 100644 --- a/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php +++ b/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php @@ -30,7 +30,7 @@ protected function enterNode(TypeNode $type): ?int return NodeVisitor::STOP_TRAVERSAL; } - if ($type instanceof TArray && $type->type_params[1]->isEmpty()) { + if ($type instanceof TArray && $type->isEmptyArray()) { $this->contains_literal = true; return NodeVisitor::STOP_TRAVERSAL; } diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index e71d91d4faf..19591a88e28 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -17,7 +17,6 @@ use Psalm\Type\Atomic\TBool; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClosure; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; @@ -306,16 +305,6 @@ public static function getScalar(): Union return new Union([$type]); } - /** - * @deprecated will be removed in Psalm 5. See getNever to retrieve a TNever that replaces TEmpty - */ - public static function getEmpty(): Union - { - $type = new TEmpty(); - - return new Union([$type]); - } - public static function getNever(): Union { $type = new TNever(); @@ -378,8 +367,8 @@ public static function getEmptyArray(): Union { $array_type = new TArray( [ - new Union([new TEmpty]), - new Union([new TEmpty]), + new Union([new TNever()]), + new Union([new TNever()]), ] ); diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 18eb806ccd5..03dbc2eb7a2 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -11,6 +11,7 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; +use Psalm\Type\Atomic\TAssertionEmpty; use Psalm\Type\Atomic\TAssertionFalsy; use Psalm\Type\Atomic\TBool; use Psalm\Type\Atomic\TCallable; @@ -25,7 +26,6 @@ use Psalm\Type\Atomic\TClosedResource; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TDependentGetClass; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEmptyNumeric; use Psalm\Type\Atomic\TEmptyScalar; @@ -231,7 +231,7 @@ public static function create( return new TNamedObject($value); case 'empty': - return $analysis_php_version_id !== null ? new TNamedObject($value) : new TEmpty(); + return $analysis_php_version_id !== null ? new TNamedObject($value) : new TAssertionEmpty(); case 'scalar': return $analysis_php_version_id !== null ? new TNamedObject($value) : new TScalar(); @@ -789,7 +789,7 @@ public function isFalsy(): bool return true; } - if ($this instanceof TArray && $this->getId() === 'array') { + if ($this instanceof TArray && $this->getId() === 'array') { return true; } diff --git a/src/Psalm/Type/Atomic/GenericTrait.php b/src/Psalm/Type/Atomic/GenericTrait.php index cbeb416155e..15d8715334e 100644 --- a/src/Psalm/Type/Atomic/GenericTrait.php +++ b/src/Psalm/Type/Atomic/GenericTrait.php @@ -99,7 +99,7 @@ public function toNamespacedString( $value_type = $this->type_params[1]; - if ($value_type->isMixed() || $value_type->isEmpty()) { + if ($value_type->isMixed() || $value_type->isNever()) { return $base_value; } diff --git a/src/Psalm/Type/Atomic/TArray.php b/src/Psalm/Type/Atomic/TArray.php index eabbe75e72b..cf8ac756c7b 100644 --- a/src/Psalm/Type/Atomic/TArray.php +++ b/src/Psalm/Type/Atomic/TArray.php @@ -91,4 +91,9 @@ public function getAssertionString(bool $exact = false): string return $this->toNamespacedString(null, [], null, false); } + + public function isEmptyArray(): bool + { + return $this->type_params[1]->isNever(); + } } diff --git a/src/Psalm/Type/Atomic/TEmpty.php b/src/Psalm/Type/Atomic/TAssertionEmpty.php similarity index 63% rename from src/Psalm/Type/Atomic/TEmpty.php rename to src/Psalm/Type/Atomic/TAssertionEmpty.php index d0fd4a78537..c6a090836f0 100644 --- a/src/Psalm/Type/Atomic/TEmpty.php +++ b/src/Psalm/Type/Atomic/TAssertionEmpty.php @@ -2,12 +2,12 @@ namespace Psalm\Type\Atomic; +use Psalm\Type\Atomic; + /** - * Denotes the `empty` type, used to describe a type corresponding to no value whatsoever. - * Empty arrays `[]` have the type `array`. - * @deprecated Will be replaced by TNever when in type context and TAssertionEmpty for assertion context in Psalm 5 + * Represents any value that returns true to empty(). This is used for assertions */ -class TEmpty extends Scalar +class TAssertionEmpty extends Atomic { public function __toString(): string { @@ -30,4 +30,9 @@ public function toPhpString( ): ?string { return null; } + + public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool + { + return false; + } } diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index 67a828515a9..be5e2e2e146 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -23,13 +23,13 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; use Psalm\Type\Atomic\TClassStringMap; -use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TObject; use Psalm\Type\Atomic\TScalar; @@ -232,7 +232,7 @@ public static function reconcileKeyedTypes( /** @psalm-suppress TypeDoesNotContainType can be empty after removing above */ if (!$result_type_candidate->getAtomicTypes()) { - $result_type_candidate->addType(new TEmpty); + $result_type_candidate->addType(new TNever()); } $orred_type = Type::combineUnionTypes( @@ -249,7 +249,7 @@ public static function reconcileKeyedTypes( throw new UnexpectedValueException('$result_type should not be null'); } - if (!$did_type_exist && $result_type->isEmpty()) { + if (!$did_type_exist && $result_type->isNever()) { continue; } @@ -654,7 +654,7 @@ private static function getValueForKey( } } elseif ($existing_key_type_part instanceof TClassStringMap) { return Type::getMixed(); - } elseif ($existing_key_type_part instanceof TEmpty + } elseif ($existing_key_type_part instanceof TNever || ($existing_key_type_part instanceof TMixed && $existing_key_type_part->from_loop_isset) ) { @@ -1018,7 +1018,7 @@ private static function adjustTKeyedArrayType( foreach ($existing_types[$base_key]->getAtomicTypes() as $base_atomic_type) { if ($base_atomic_type instanceof TKeyedArray || ($base_atomic_type instanceof TArray - && !$base_atomic_type->type_params[1]->isEmpty()) + && !$base_atomic_type->isEmptyArray()) || $base_atomic_type instanceof TList || $base_atomic_type instanceof TClassStringMap ) { @@ -1035,7 +1035,7 @@ private static function adjustTKeyedArrayType( null ); - if (!$previous_key_type->isEmpty()) { + if (!$previous_key_type->isNever()) { $base_atomic_type->previous_key_type = $previous_key_type; } $base_atomic_type->previous_value_type = $previous_value_type; diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 5972e03a12a..843fd742dd7 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -1048,11 +1048,6 @@ public function isGenerator(): bool && ($single_type->value === 'Generator'); } - public function isEmpty(): bool - { - return isset($this->types['empty']) && count($this->types) === 1; - } - public function substitute(Union $old_type, ?Union $new_type = null): void { if ($this->hasMixed() && !$this->isEmptyMixed()) { diff --git a/stubs/CoreGenericFunctions.phpstub b/stubs/CoreGenericFunctions.phpstub index e629e571fd9..24352529233 100644 --- a/stubs/CoreGenericFunctions.phpstub +++ b/stubs/CoreGenericFunctions.phpstub @@ -136,7 +136,7 @@ function array_flip(array $array) * * @param TArray $array * - * @return (TArray is array ? null : TKey|null) + * @return (TArray is array ? null : TKey|null) * @psalm-pure * @psalm-ignore-nullable-return */ @@ -150,7 +150,7 @@ function key($array) * * @param TArray $array * - * @return (TArray is array ? null : (TArray is non-empty-array ? TKey : TKey|null)) + * @return (TArray is array ? null : (TArray is non-empty-array ? TKey : TKey|null)) * @psalm-pure */ function array_key_first($array) @@ -163,7 +163,7 @@ function array_key_first($array) * * @param TArray $array * - * @return (TArray is array ? null : (TArray is non-empty-array ? TKey : TKey|null)) + * @return (TArray is array ? null : (TArray is non-empty-array ? TKey : TKey|null)) * @psalm-pure */ function array_key_last($array) diff --git a/stubs/CoreGenericIterators.phpstub b/stubs/CoreGenericIterators.phpstub index c20e54de882..34ad308d324 100644 --- a/stubs/CoreGenericIterators.phpstub +++ b/stubs/CoreGenericIterators.phpstub @@ -442,15 +442,15 @@ class DirectoryIterator extends SplFileInfo implements SeekableIterator { } /** - * @template-implements Iterator + * @template-implements Iterator */ class EmptyIterator implements Iterator { /** - * @return empty + * @return never */ public function current() {} /** - * @return empty + * @return never */ public function key() {} /** diff --git a/tests/ArrayAssignmentTest.php b/tests/ArrayAssignmentTest.php index 5a7c7f6c1ec..e9104c2e1f9 100644 --- a/tests/ArrayAssignmentTest.php +++ b/tests/ArrayAssignmentTest.php @@ -159,7 +159,7 @@ class B {} $foo = []; $foo[][] = "hello";', 'assertions' => [ - '$foo' => 'non-empty-list>', + '$foo' => 'non-empty-list>', ], ], 'implicit3dIntArrayCreation' => [ @@ -167,7 +167,7 @@ class B {} $foo = []; $foo[][][] = "hello";', 'assertions' => [ - '$foo' => 'non-empty-list>>', + '$foo' => 'non-empty-list>>', ], ], 'implicit4dIntArrayCreation' => [ @@ -175,7 +175,7 @@ class B {} $foo = []; $foo[][][][] = "hello";', 'assertions' => [ - '$foo' => 'non-empty-list>>>', + '$foo' => 'non-empty-list>>>', ], ], 'implicitIndexedIntArrayCreation' => [ @@ -1008,7 +1008,7 @@ function updateArray(array $arr) : array { $b = (array) null;', 'assertions' => [ '$a' => 'array{0?: int, 1?: string}', - '$b' => 'array', + '$b' => 'array', ], ], 'getOnCoercedArray' => [ @@ -1124,7 +1124,7 @@ function takesList(array $arr) : void {} takesList($a);', 'assertions' => [ - '$a' => 'array' + '$a' => 'array' ], ], 'listCreatedInSingleStatementUsedAsArray' => [ diff --git a/tests/ArrayFunctionCallTest.php b/tests/ArrayFunctionCallTest.php index f504a6f0def..39151240442 100644 --- a/tests/ArrayFunctionCallTest.php +++ b/tests/ArrayFunctionCallTest.php @@ -572,8 +572,8 @@ function foo(array $arr) { $rightCount = [1, 2, 3]; assert (1 > count($rightCount));', 'assertions' => [ - '$leftCount' => 'array', - '$rightCount' => 'array', + '$leftCount' => 'array', + '$rightCount' => 'array', ], ], 'arrayEmptyArrayAfterCountLessThanEqualToZero' => [ @@ -585,8 +585,8 @@ function foo(array $arr) { $rightCount = [1, 2, 3]; assert (0 >= count($rightCount));', 'assertions' => [ - '$leftCount' => 'array', - '$rightCount' => 'array', + '$leftCount' => 'array', + '$rightCount' => 'array', ], ], 'arrayNotNonEmptyArrayAfterCountGreaterThanEqualToZero' => [ @@ -774,7 +774,7 @@ function test(): void { } }', 'assertions' => [], - 'error_levels' => ['MixedAssignment', 'MixedArgument', 'MixedArgumentTypeCoercion'], + 'error_levels' => ['MixedAssignment', 'MixedArgument', 'MixedArgumentTypeCoercion', 'NoValue'], ], 'arrayPopNotNullable' => [ ' [ ' */ + /** @return list */ function makeArray(): array { return []; } $a = makeArray(); $b = reset($a);', @@ -1246,7 +1246,7 @@ function makeArray(): array { return [1, 3]; } ], 'arrayEndEmptyList' => [ ' */ + /** @return list */ function makeArray(): array { return []; } $a = makeArray(); $b = end($a);', @@ -2033,7 +2033,7 @@ function thing() { 'arrayShiftOnMixedOrEmptyArray' => [ ' $lengths + * @param mixed|array $lengths */ function doStuff($lengths): void { /** @psalm-suppress MixedArgument, MixedAssignment */ diff --git a/tests/ConstantTest.php b/tests/ConstantTest.php index 1aefeb093bc..04139decfe3 100644 --- a/tests/ConstantTest.php +++ b/tests/ConstantTest.php @@ -303,7 +303,7 @@ class C extends B { public const ARR = [...parent::ARR]; } - /** @param array $arg */ + /** @param array $arg */ function foo(array $arg): void {} foo(C::ARR); ', diff --git a/tests/EnumTest.php b/tests/EnumTest.php index 38b58bc204a..4cf234557a0 100644 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -274,7 +274,7 @@ enum Status: int {} $_z = Status::cases(); ', 'assertions' => [ - '$_z===' => 'array', + '$_z===' => 'array', ], [], '8.1', diff --git a/tests/FileManipulation/MissingReturnTypeTest.php b/tests/FileManipulation/MissingReturnTypeTest.php index 4448a04f0fc..7bf42b0d1cc 100644 --- a/tests/FileManipulation/MissingReturnTypeTest.php +++ b/tests/FileManipulation/MissingReturnTypeTest.php @@ -847,7 +847,7 @@ function foo() { }', ' + * @psalm-return array */ function foo(): array { return []; diff --git a/tests/FileManipulation/ReturnTypeManipulationTest.php b/tests/FileManipulation/ReturnTypeManipulationTest.php index 94bacfb1706..415da9b55d5 100644 --- a/tests/FileManipulation/ReturnTypeManipulationTest.php +++ b/tests/FileManipulation/ReturnTypeManipulationTest.php @@ -651,7 +651,7 @@ function get_form_fields(string $a) { /** * @param string $a * - * @psalm-return array + * @psalm-return array */ function get_form_fields(string $a): array { switch($a){ diff --git a/tests/FunctionCallTest.php b/tests/FunctionCallTest.php index 4835ce61e91..5632c5d2bd0 100644 --- a/tests/FunctionCallTest.php +++ b/tests/FunctionCallTest.php @@ -188,7 +188,7 @@ class B extends A { exit; }', 'assertions' => [ - '$a' => 'array', + '$a' => 'array', ], ], 'byRefAfterCallable' => [ @@ -1166,7 +1166,7 @@ function example($x) : int { ' $x + * @param array $x * @return 0 */ function example($x) : int { diff --git a/tests/IntRangeTest.php b/tests/IntRangeTest.php index 5b7cf913eaf..62ec031b628 100644 --- a/tests/IntRangeTest.php +++ b/tests/IntRangeTest.php @@ -203,21 +203,27 @@ function getInt(): int{return 0;} $h = $d % $e; $i = -3 % $a; $j = -3 % $b; + /** @psalm-suppress NoValue */ $k = -3 % $c; $l = -3 % $d; $m = 3 % $a; $n = 3 % $b; + /** @psalm-suppress NoValue */ $o = 3 % $c; $p = 3 % $d; + /** @psalm-suppress NoValue */ $q = $a % 0; $r = $a % 3; $s = $a % -3; + /** @psalm-suppress NoValue */ $t = $b % 0; $u = $b % 3; $v = $b % -3; + /** @psalm-suppress NoValue */ $w = $c % 0; $x = $c % 3; $y = $c % -3; + /** @psalm-suppress NoValue */ $z = $d % 0; $aa = $d % 3; $ab = $d % -3; @@ -228,22 +234,22 @@ function getInt(): int{return 0;} '$h===' => 'int<-4, 4>', '$i===' => 'int', '$j===' => 'int', - '$k===' => 'empty', + '$k===' => 'never', '$l===' => 'int', '$m===' => 'int<0, max>', '$n===' => 'int', - '$o===' => 'empty', + '$o===' => 'never', '$p===' => 'int', - '$q===' => 'empty', + '$q===' => 'never', '$r===' => 'int<0, 2>', '$s===' => 'int<-2, 0>', - '$t===' => 'empty', + '$t===' => 'never', '$u===' => 'int<-2, 0>', '$v===' => 'int<2, 0>', - '$w===' => 'empty', + '$w===' => 'never', '$x===' => 'int<0, 2>', '$y===' => 'int<-2, 0>', - '$z===' => 'empty', + '$z===' => 'never', '$aa===' => 'int<-2, 2>', '$ab===' => 'int<-2, 2>', ] @@ -285,9 +291,9 @@ function getInt(): int{return 0;} ', 'assertions' => [ '$e===' => '0', - '$f===' => 'empty', + '$f===' => 'float', '$g===' => '1', - '$h===' => 'empty', + '$h===' => '0|1|float', '$i===' => 'int', '$j===' => 'float', '$k===' => '-1', @@ -304,7 +310,7 @@ function getInt(): int{return 0;} '$v===' => 'float', '$w===' => '1', '$x===' => '0', - '$y===' => 'empty', + '$y===' => 'float', '$z===' => '1', '$aa===' => 'int<1, max>', '$ab===' => 'float', diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 8676075fd61..4349ce74281 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -935,7 +935,7 @@ final public function getValue() { ' 'SplObjectStorage', + '$a' => 'SplObjectStorage', ] ], 'allowIteratorToBeNull' => [ diff --git a/tests/ReturnTypeProvider/GetObjectVarsTest.php b/tests/ReturnTypeProvider/GetObjectVarsTest.php index 9e8d8500e47..3389bbd4de2 100644 --- a/tests/ReturnTypeProvider/GetObjectVarsTest.php +++ b/tests/ReturnTypeProvider/GetObjectVarsTest.php @@ -33,7 +33,7 @@ class C { } $ret = get_object_vars(new C); ', - ['$ret' => 'array'], + ['$ret' => 'array'], ]; yield 'includesPrivateAndProtectedPropertiesWhenCalledInsideClassScope' => [ diff --git a/tests/Template/ClassTemplateTest.php b/tests/Template/ClassTemplateTest.php index 10204ede0a6..290fc015cd4 100644 --- a/tests/Template/ClassTemplateTest.php +++ b/tests/Template/ClassTemplateTest.php @@ -2506,7 +2506,7 @@ public function add($key, $t) : void { } }', [ - '$a' => 'ArrayCollection' + '$a' => 'ArrayCollection' ] ], 'newGenericBecomesPropertyTypeValidArg' => [ diff --git a/tests/TypeCombinationTest.php b/tests/TypeCombinationTest.php index 1f3350f3bfb..03737402753 100644 --- a/tests/TypeCombinationTest.php +++ b/tests/TypeCombinationTest.php @@ -109,10 +109,10 @@ public function providerTestValidTypeCombination(): array 'null', ], ], - 'mixedOrEmpty' => [ + 'mixedOrNever' => [ 'mixed', [ - 'empty', + 'never', 'mixed', ], ], @@ -124,10 +124,10 @@ public function providerTestValidTypeCombination(): array ], ], 'mixedOrEmptyArray' => [ - 'array|mixed', + 'array|mixed', [ 'mixed', - 'array', + 'array', ], ], 'falseTrueToBool' => [ @@ -197,16 +197,16 @@ public function providerTestValidTypeCombination(): array ], ], 'emptyArrays' => [ - 'array', + 'array', [ - 'array', - 'array', + 'array', + 'array', ], ], 'arrayStringOrEmptyArray' => [ 'array', [ - 'array', + 'array', 'array', ], ], @@ -227,7 +227,7 @@ public function providerTestValidTypeCombination(): array 'arrayMixedOrEmpty' => [ 'array', [ - 'array', + 'array', 'array', ], ], @@ -309,16 +309,16 @@ public function providerTestValidTypeCombination(): array ], ], 'arrayObjectAndParamsWithEmptyArray' => [ - 'ArrayObject|array', + 'ArrayObject|array', [ 'ArrayObject', - 'array', + 'array', ], ], 'emptyArrayWithArrayObjectAndParams' => [ - 'ArrayObject|array', + 'ArrayObject|array', [ - 'array', + 'array', 'ArrayObject', ], ], diff --git a/tests/TypeComparatorTest.php b/tests/TypeComparatorTest.php index 714a786b32e..1092cdfb11f 100644 --- a/tests/TypeComparatorTest.php +++ b/tests/TypeComparatorTest.php @@ -114,19 +114,19 @@ public function getAllowedChildTypes(): array ], 'listAcceptsEmptyArray' => [ 'list', - 'array', + 'array', ], 'arrayAcceptsEmptyArray' => [ 'array', - 'array', + 'array', ], 'arrayOptionalKeyed1AcceptsEmptyArray' => [ 'array{foo?: string}', - 'array', + 'array', ], 'arrayOptionalKeyed2AcceptsEmptyArray' => [ 'array{foo?: string}&array', - 'array', + 'array', ], 'Lowercase-stringAndCallable-string' => [ 'lowercase-string', diff --git a/tests/TypeReconciliation/ReconcilerTest.php b/tests/TypeReconciliation/ReconcilerTest.php index fd1fdb8eba3..f592a750f83 100644 --- a/tests/TypeReconciliation/ReconcilerTest.php +++ b/tests/TypeReconciliation/ReconcilerTest.php @@ -104,7 +104,7 @@ public function providerTestReconcilation(): array 'nullWithSomeClassPipeNull' => ['null', 'null', 'SomeClass|null'], 'nullWithMixed' => ['null', 'null', 'mixed'], - 'falsyWithSomeClass' => ['empty', 'falsy', 'SomeClass'], + 'falsyWithSomeClass' => ['never', 'falsy', 'SomeClass'], 'falsyWithSomeClassPipeFalse' => ['false', 'falsy', 'SomeClass|false'], 'falsyWithSomeClassPipeBool' => ['false', 'falsy', 'SomeClass|bool'], 'falsyWithMixed' => ['empty-mixed', 'falsy', 'mixed'], @@ -176,7 +176,7 @@ public function providerTestTypeIsContainedBy(): array 'array>', ], 'objectLikeTypeWithPossiblyUndefinedToEmpty' => [ - 'array', + 'array', 'array{a?: string, b?: string}', ], 'literalNumericStringInt' => [ diff --git a/tests/TypeReconciliation/TypeTest.php b/tests/TypeReconciliation/TypeTest.php index 6908b92dd48..d6470487776 100644 --- a/tests/TypeReconciliation/TypeTest.php +++ b/tests/TypeReconciliation/TypeTest.php @@ -592,7 +592,7 @@ public function barBar(One $one = null) { $ids = []; }', 'assertions' => [ - '$ids' => 'array', + '$ids' => 'array', ], ], 'arrayUnionTypeAssertionWithIsArray' => [ @@ -603,7 +603,7 @@ public function barBar(One $one = null) { $ids = []; }', 'assertions' => [ - '$ids' => 'array', + '$ids' => 'array', ], ], '2dArrayUnionTypeAssertionWithIsArray' => [