diff --git a/README.md b/README.md index c136915f590..5bdef923021 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,6 @@ Have a look at [CONTRIBUTING.md](CONTRIBUTING.md). Built by Matt Brown ([@muglug](https://github.com/muglug)). -Maintained by Orklah ([@orklah](https://github.com/orklah)) and Bruce Weirdan ([@weirdan](https://github.com/weirdan)). +Maintained by Orklah ([@orklah](https://github.com/orklah)), Daniil Gentili ([@danog](https://github.com/danog)), and Bruce Weirdan ([@weirdan](https://github.com/weirdan)). The engineering team at [Vimeo](https://github.com/vimeo) have provided a lot encouragement, especially [@nbeliard](https://github.com/nbeliard), [@erunion](https://github.com/erunion) and [@nickyr](https://github.com/nickyr). diff --git a/UPGRADING.md b/UPGRADING.md index 6e176c69ebd..f1d3a10fad7 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,25 +1,41 @@ # Upgrading from Psalm 4 to Psalm 5 ## Changed -- [BC] `Psalm\Type\Union`s are now partially immutable, mutator methods were removed and moved into `Psalm\Type\MutableUnion`. - To modify a union type, use the new `Psalm\Type\Union::getBuilder` method to turn a `Psalm\Type\Union` into a `Psalm\Type\MutableUnion`: once you're done, use `Psalm\Type\MutableUnion::freeze` to get a new `Psalm\Type\Union`. + +- [BC] Shaped arrays can now be sealed: this brings many assertion improvements and bugfixes, see [the docs for more info](https://psalm.dev/docs/annotating_code/type_syntax/array_types/#sealed-object-like-arrays). + +- [BC] All atomic types, `Psalm\Type\Union`, `Psalm\CodeLocation` and storages are fully immutable, use the new setter methods or the new constructors to change properties: these setter methods will return new instances without altering the original instance. + Full immutability fixes a whole class of bugs that occurred in multithreaded mode, you can now feel free to use `--threads=$(nproc)` ;) + Full immutability also makes Psalm run faster, even in single-threaded mode, by removing all superfluous `clone`s! + For this purpose, `__clone` was also made private, forbidding the cloning of atomics, unions and storages (an old and brittle pattern used to avoid side-effects caused by mutability). + +- [BC] `Psalm\Type\Union`s are now fully immutable, pre-existing in-place mutator methods were removed and moved into `Psalm\Type\MutableUnion`. + To modify a union type, usage of the new setter methods in `Psalm\Type\Union` is strongly recommended. + When many consecutive property sets are required, use `Psalm\Type\Union::setProperties` method to avoid creating a new instance for each set. + All setter methods will return a new instance of the type without altering the original instance. + If many property sets are required throughout multiple methods on a single Union instance, use `Psalm\Type\Union::getBuilder` to turn a `Psalm\Type\Union` into a `Psalm\Type\MutableUnion`: once you're done, use `Psalm\Type\MutableUnion::freeze` to get a new `Psalm\Type\Union`. Methods removed from `Psalm\Type\Union` and moved into `Psalm\Type\MutableUnion`: + - `replaceTypes` - `addType` - `removeType` - `substitute` - `replaceClassLike` +- [BC] `Psalm\Type\TypeNode::getChildNodes()` was removed, use `Psalm\Type\Union::getAtomicTypes()` to get the types of a union, and use `Psalm\Type\TypeVisitor` with the new `Psalm\Type\MutableTypeVisitor` class to iterate over a type tree. + +- [BC] `Psalm\Type\TypeVisitor` is now fully immutable, implementors MUST NOT alter type nodes during iteration: use `Psalm\Type\MutableTypeVisitor` if type node mutation is desired. + - [BC] TPositiveInt has been removed and replaced by TIntRange - [BC] The parameter `$php_version` of `Psalm\Type\Atomic::create()` renamed to `$analysis_php_version_id` and changed from `array|null` to `int|null`. - Previously it accepted PHP version as `array{major_version, minor_version}` + Previously it accepted PHP version as `strict-array{major_version, minor_version}` while now it accepts version ID, similar to how [`PHP_VERSION_ID` is calculated](https://www.php.net/manual/en/reserved.constants.php#constant.php-version-id). - [BC] The parameter `$php_version` of `Psalm\Type::parseString()` renamed to `$analysis_php_version_id` and changed from `array|null` to `int|null`. - Previously it accepted PHP version as `array{major_version, minor_version}` + Previously it accepted PHP version as `strict-array{major_version, minor_version}` while now it accepts version ID. - [BC] Parameter 0 of `canBeFullyExpressedInPhp()` of the classes listed below @@ -789,7 +805,7 @@ - [BC] Class `Psalm\Type\TaintKind` became final - [BC] Class `Psalm\Type\Union` became final - [BC] Property `Psalm\Config::$universal_object_crates` changed default value - from `array{'stdClass','SimpleXMLElement','SimpleXMLIterator'}` to `null` + from `strict-array{'stdClass','SimpleXMLElement','SimpleXMLIterator'}` to `null` ## Removed - [BC] Property `Psalm\Codebase::$php_major_version` was removed, use diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 2c7587ff2b4..e2e89cfaef8 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -49,11 +49,11 @@ * * In Phan 0.12.3, * - * - This started using array shapes for union types (array{...}). + * - This started using array shapes for union types (strict-array{...}). * * \Phan\Language\UnionType->withFlattenedArrayShapeOrLiteralTypeInstances() may be of help to programmatically convert these to array|array * - * - This started using array shapes with optional fields for union types (array{key?:int}). + * - This started using array shapes with optional fields for union types (strict-array{key?:int}). * A `?` after the array shape field's key indicates that the field is optional. * * - This started adding param signatures and return signatures to `callable` types. @@ -346,7 +346,7 @@ 'ArgumentCountError::getLine' => ['int'], 'ArgumentCountError::getMessage' => ['string'], 'ArgumentCountError::getPrevious' => ['?Throwable'], -'ArgumentCountError::getTrace' => ['list\',args?:array}>'], +'ArgumentCountError::getTrace' => ['list\',args?:array}>'], 'ArgumentCountError::getTraceAsString' => ['string'], 'ArithmeticError::__clone' => ['void'], 'ArithmeticError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?Error'], @@ -357,7 +357,7 @@ 'ArithmeticError::getLine' => ['int'], 'ArithmeticError::getMessage' => ['string'], 'ArithmeticError::getPrevious' => ['?Throwable'], -'ArithmeticError::getTrace' => ['list\',args?:array}>'], +'ArithmeticError::getTrace' => ['list\',args?:array}>'], 'ArithmeticError::getTraceAsString' => ['string'], 'array_change_key_case' => ['associative-array', 'array'=>'array', 'case='=>'int'], 'array_chunk' => ['list', 'array'=>'array', 'length'=>'int', 'preserve_keys='=>'bool'], @@ -501,7 +501,7 @@ 'BadFunctionCallException::getLine' => ['int'], 'BadFunctionCallException::getMessage' => ['string'], 'BadFunctionCallException::getPrevious' => ['?Throwable|?BadFunctionCallException'], -'BadFunctionCallException::getTrace' => ['list\',args?:array}>'], +'BadFunctionCallException::getTrace' => ['list\',args?:array}>'], 'BadFunctionCallException::getTraceAsString' => ['string'], 'BadMethodCallException::__clone' => ['void'], 'BadMethodCallException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?BadMethodCallException'], @@ -511,7 +511,7 @@ 'BadMethodCallException::getLine' => ['int'], 'BadMethodCallException::getMessage' => ['string'], 'BadMethodCallException::getPrevious' => ['?Throwable|?BadMethodCallException'], -'BadMethodCallException::getTrace' => ['list\',args?:array}>'], +'BadMethodCallException::getTrace' => ['list\',args?:array}>'], 'BadMethodCallException::getTraceAsString' => ['string'], 'base64_decode' => ['string|false', 'string'=>'string', 'strict='=>'bool'], 'base64_encode' => ['string', 'string'=>'string'], @@ -1001,7 +1001,7 @@ 'CairoSvgSurface::restrictToVersion' => ['void', 'version'=>'string'], 'CairoSvgSurface::versionToString' => ['string', 'version'=>'int'], 'cal_days_in_month' => ['int', 'calendar'=>'int', 'month'=>'int', 'year'=>'int'], -'cal_from_jd' => ['false|array{date:string,month:int,day:int,year:int,dow:int,abbrevdayname:string,dayname:string,abbrevmonth:string,monthname:string}', 'julian_day'=>'int', 'calendar'=>'int'], +'cal_from_jd' => ['false|strict-array{date:string,month:int,day:int,year:int,dow:int,abbrevdayname:string,dayname:string,abbrevmonth:string,monthname:string}', 'julian_day'=>'int', 'calendar'=>'int'], 'cal_info' => ['array', 'calendar='=>'int'], 'cal_to_jd' => ['int', 'calendar'=>'int', 'month'=>'int', 'day'=>'int', 'year'=>'int'], 'calcul_hmac' => ['string', 'clent'=>'string', 'siretcode'=>'string', 'price'=>'string', 'reference'=>'string', 'validity'=>'string', 'taxation'=>'string', 'devise'=>'string', 'language'=>'string'], @@ -1075,7 +1075,7 @@ 'ClosedGeneratorException::getLine' => ['int'], 'ClosedGeneratorException::getMessage' => ['string'], 'ClosedGeneratorException::getPrevious' => ['Throwable|ClosedGeneratorException|null'], -'ClosedGeneratorException::getTrace' => ['list\',args?:array}>'], +'ClosedGeneratorException::getTrace' => ['list\',args?:array}>'], 'ClosedGeneratorException::getTraceAsString' => ['string'], 'closedir' => ['void', 'dir_handle='=>'resource'], 'closelog' => ['bool'], @@ -1723,7 +1723,7 @@ 'date_default_timezone_set' => ['bool', 'timezoneId'=>'string'], 'date_diff' => ['DateInterval|false', 'baseObject'=>'DateTimeInterface', 'targetObject'=>'DateTimeInterface', 'absolute='=>'bool'], 'date_format' => ['string', 'object'=>'DateTimeInterface', 'format'=>'string'], -'date_get_last_errors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], +'date_get_last_errors' => ['strict-array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], 'date_interval_create_from_date_string' => ['DateInterval', 'datetime'=>'string'], 'date_interval_format' => ['string', 'object'=>'DateInterval', 'format'=>'string'], 'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int|mixed'], @@ -1782,7 +1782,7 @@ 'DateTime::createFromInterface' => ['static', 'object' => 'DateTimeInterface'], 'DateTime::diff' => ['DateInterval|false', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTime::format' => ['string', 'format'=>'string'], -'DateTime::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], +'DateTime::getLastErrors' => ['strict-array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], 'DateTime::getOffset' => ['int'], 'DateTime::getTimestamp' => ['int'], 'DateTime::getTimezone' => ['DateTimeZone|false'], @@ -1796,7 +1796,7 @@ 'DateTimeImmutable::__set_state' => ['static', 'array'=>'array'], 'DateTimeImmutable::__wakeup' => ['void'], 'DateTimeImmutable::createFromInterface' => ['static', 'object' => 'DateTimeInterface'], -'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], +'DateTimeImmutable::getLastErrors' => ['strict-array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], 'DateTimeInterface::getOffset' => ['int'], @@ -1808,8 +1808,8 @@ 'DateTimeZone::getLocation' => ['array|false'], 'DateTimeZone::getName' => ['string'], 'DateTimeZone::getOffset' => ['int|false', 'datetime'=>'DateTimeInterface'], -'DateTimeZone::getTransitions' => ['list|false', 'timestamp_begin='=>'int', 'timestamp_end='=>'int'], -'DateTimeZone::listAbbreviations' => ['array>|false'], +'DateTimeZone::getTransitions' => ['list|false', 'timestamp_begin='=>'int', 'timestamp_end='=>'int'], +'DateTimeZone::listAbbreviations' => ['array>|false'], 'DateTimeZone::listIdentifiers' => ['list', 'timezoneGroup='=>'int', 'countryCode='=>'string|null'], 'db2_autocommit' => ['mixed', 'connection'=>'resource', 'value='=>'int'], 'db2_bind_param' => ['bool', 'stmt'=>'resource', 'parameter_number'=>'int', 'variable_name'=>'string', 'parameter_type='=>'int', 'data_type='=>'int', 'precision='=>'int', 'scale='=>'int'], @@ -1953,7 +1953,7 @@ 'dcgettext' => ['string', 'domain'=>'string', 'message'=>'string', 'category'=>'int'], 'dcngettext' => ['string', 'domain'=>'string', 'singular'=>'string', 'plural'=>'string', 'count'=>'int', 'category'=>'int'], 'deaggregate' => ['', 'object'=>'object', 'class_name='=>'string'], -'debug_backtrace' => ['list', 'options='=>'int', 'limit='=>'int'], +'debug_backtrace' => ['list', 'options='=>'int', 'limit='=>'int'], 'debug_print_backtrace' => ['void', 'options='=>'int', 'limit='=>'int'], 'debug_zval_dump' => ['void', '...value'=>'mixed'], 'debugger_connect' => [''], @@ -2049,7 +2049,7 @@ 'DomainException::getLine' => ['int'], 'DomainException::getMessage' => ['string'], 'DomainException::getPrevious' => ['Throwable|DomainException|null'], -'DomainException::getTrace' => ['list\',args?:array}>'], +'DomainException::getTrace' => ['list\',args?:array}>'], 'DomainException::getTraceAsString' => ['string'], 'DOMAttr::__construct' => ['void', 'name'=>'string', 'value='=>'string'], 'DOMAttr::getLineNo' => ['int'], @@ -2488,7 +2488,7 @@ 'enchant_broker_get_dict_path' => ['string', 'broker'=>'resource', 'type'=>'int'], 'enchant_broker_get_error' => ['string|false', 'broker'=>'resource'], 'enchant_broker_init' => ['resource|false'], -'enchant_broker_list_dicts' => ['array|false', 'broker'=>'resource'], +'enchant_broker_list_dicts' => ['array|false', 'broker'=>'resource'], 'enchant_broker_request_dict' => ['resource|false', 'broker'=>'resource', 'tag'=>'string'], 'enchant_broker_request_pwl_dict' => ['resource|false', 'broker'=>'resource', 'filename'=>'string'], 'enchant_broker_set_dict_path' => ['bool', 'broker'=>'resource', 'type'=>'int', 'path'=>'string'], @@ -2512,10 +2512,10 @@ 'Error::getLine' => ['int'], 'Error::getMessage' => ['string'], 'Error::getPrevious' => ['Throwable|Error|null'], -'Error::getTrace' => ['list\',args?:array}>'], +'Error::getTrace' => ['list\',args?:array}>'], 'Error::getTraceAsString' => ['string'], 'error_clear_last' => ['void'], -'error_get_last' => ['?array{type:int,message:string,file:string,line:int}'], +'error_get_last' => ['?strict-array{type:int,message:string,file:string,line:int}'], 'error_log' => ['bool', 'message'=>'string', 'message_type='=>'int', 'destination='=>'string', 'additional_headers='=>'string'], 'error_reporting' => ['int', 'error_level='=>'int'], 'ErrorException::__clone' => ['void'], @@ -2527,7 +2527,7 @@ 'ErrorException::getMessage' => ['string'], 'ErrorException::getPrevious' => ['Throwable|ErrorException|null'], 'ErrorException::getSeverity' => ['int'], -'ErrorException::getTrace' => ['list\',args?:array}>'], +'ErrorException::getTrace' => ['list\',args?:array}>'], 'ErrorException::getTraceAsString' => ['string'], 'escapeshellarg' => ['string', 'arg'=>'string'], 'escapeshellcmd' => ['string', 'command'=>'string'], @@ -2895,7 +2895,7 @@ 'Exception::getLine' => ['int'], 'Exception::getMessage' => ['string'], 'Exception::getPrevious' => ['?Throwable|?Exception'], -'Exception::getTrace' => ['list\',args?:array}>'], +'Exception::getTrace' => ['list\',args?:array}>'], 'Exception::getTraceAsString' => ['string'], 'exec' => ['string|false', 'command'=>'string', '&w_output='=>'array', '&w_result_code='=>'int'], 'exif_imagetype' => ['int|false', 'filename'=>'string'], @@ -3207,7 +3207,7 @@ 'ffmpeg_movie::hasAudio' => ['bool'], 'ffmpeg_movie::hasVideo' => ['bool'], 'fgetc' => ['string|false', 'stream'=>'resource'], -'fgetcsv' => ['list|array{0: null}|false|null', 'stream'=>'resource', 'length='=>'int', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'fgetcsv' => ['list|strict-array{0: null}|false|null', 'stream'=>'resource', 'length='=>'int', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'fgets' => ['string|false', 'stream'=>'resource', 'length='=>'int'], 'fgetss' => ['string|false', 'fp'=>'resource', 'length='=>'int', 'allowable_tags='=>'string'], 'file' => ['list|false', 'filename'=>'string', 'flags='=>'int', 'context='=>'resource'], @@ -3315,7 +3315,7 @@ 'fscanf\'1' => ['int', 'stream'=>'resource', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'fseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], 'fsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float'], -'fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'stream'=>'resource'], +'fstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'stream'=>'resource'], 'ftell' => ['int|false', 'stream'=>'resource'], 'ftok' => ['int', 'filename'=>'string', 'project_id'=>'string'], 'ftp_alloc' => ['bool', 'ftp'=>'FTP\Connection', 'size'=>'int', '&w_response='=>'string'], @@ -3377,7 +3377,7 @@ 'gc_enable' => ['void'], 'gc_enabled' => ['bool'], 'gc_mem_caches' => ['int'], -'gc_status' => ['array{runs:int,collected:int,threshold:int,roots:int}'], +'gc_status' => ['strict-array{runs:int,collected:int,threshold:int,roots:int}'], 'gd_info' => ['array'], 'gearman_bugreport' => [''], 'gearman_client_add_options' => ['', 'client_object'=>'', 'option'=>''], @@ -3644,7 +3644,7 @@ 'GEOSGeometry::equals' => ['bool', 'geom'=>'GEOSGeometry'], 'GEOSGeometry::equalsExact' => ['bool', 'geom'=>'GEOSGeometry', 'tolerance'=>'float'], 'GEOSGeometry::isEmpty' => ['bool'], -'GEOSGeometry::checkValidity' => ['array{valid: bool, reason?: string, location?: GEOSGeometry}'], +'GEOSGeometry::checkValidity' => ['strict-array{valid: bool, reason?: string, location?: GEOSGeometry}'], 'GEOSGeometry::isSimple' => ['bool'], 'GEOSGeometry::isRing' => ['bool'], 'GEOSGeometry::hasZ' => ['bool'], @@ -3676,7 +3676,7 @@ 'GEOSGeometry::delaunayTriangulation' => ['GEOSGeometry', 'tolerance'=>'float', 'onlyEdges'=>'bool'], 'GEOSGeometry::voronoiDiagram' => ['GEOSGeometry', 'tolerance'=>'float', 'onlyEdges'=>'bool', 'extent'=>'GEOSGeometry|null'], 'GEOSLineMerge' => ['array', 'geom'=>'GEOSGeometry'], -'GEOSPolygonize' => ['array{rings: GEOSGeometry[], cut_edges?: GEOSGeometry[], dangles: GEOSGeometry[], invalid_rings: GEOSGeometry[]}', 'geom'=>'GEOSGeometry'], +'GEOSPolygonize' => ['strict-array{rings: GEOSGeometry[], cut_edges?: GEOSGeometry[], dangles: GEOSGeometry[], invalid_rings: GEOSGeometry[]}', 'geom'=>'GEOSGeometry'], 'GEOSRelateMatch' => ['bool', 'matrix'=>'string', 'pattern'=>'string'], 'GEOSSharedPaths' => ['GEOSGeometry', 'geom1'=>'GEOSGeometry', 'geom2'=>'GEOSGeometry'], 'GEOSVersion' => ['string'], @@ -3733,7 +3733,7 @@ 'get_resources' => ['array', 'type='=>'string'], 'getallheaders' => ['array|false'], 'getcwd' => ['string|false'], -'getdate' => ['array{seconds: int<0, 59>, minutes: int<0, 59>, hours: int<0, 23>, mday: int<1, 31>, wday: int<0, 6>, mon: int<1, 12>, year: int, yday: int<0, 365>, weekday: "Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday", month: "January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December", 0: int}', 'timestamp='=>'int'], +'getdate' => ['strict-array{seconds: int<0, 59>, minutes: int<0, 59>, hours: int<0, 23>, mday: int<1, 31>, wday: int<0, 6>, mon: int<1, 12>, year: int, yday: int<0, 365>, weekday: "Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday", month: "January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December", 0: int}', 'timestamp='=>'int'], 'getenv' => ['string|false', 'name'=>'string', 'local_only='=>'bool'], 'getenv\'1' => ['array'], 'gethostbyaddr' => ['string|false', 'ip'=>'string'], @@ -3994,7 +3994,7 @@ 'gmp_com' => ['GMP', 'num'=>'GMP|string|int'], 'gmp_div' => ['GMP', 'num1'=>'GMP|resource|string', 'num2'=>'GMP|resource|string', 'rounding_mode='=>'int'], 'gmp_div_q' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int', 'rounding_mode='=>'int'], -'gmp_div_qr' => ['array{0: GMP, 1: GMP}', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int', 'rounding_mode='=>'int'], +'gmp_div_qr' => ['strict-array{0: GMP, 1: GMP}', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int', 'rounding_mode='=>'int'], 'gmp_div_r' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int', 'rounding_mode='=>'int'], 'gmp_divexact' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_export' => ['string|false', 'num'=>'GMP|string|int', 'word_size='=>'int', 'flags='=>'int'], @@ -4025,13 +4025,13 @@ 'gmp_random_range' => ['GMP', 'min'=>'GMP|string|int', 'max'=>'GMP|string|int'], 'gmp_random_seed' => ['void', 'seed'=>'GMP|string|int'], 'gmp_root' => ['GMP', 'num'=>'GMP|string|int', 'nth'=>'int'], -'gmp_rootrem' => ['array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int', 'nth'=>'int'], +'gmp_rootrem' => ['strict-array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int', 'nth'=>'int'], 'gmp_scan0' => ['int', 'num1'=>'GMP|string|int', 'start'=>'int'], 'gmp_scan1' => ['int', 'num1'=>'GMP|string|int', 'start'=>'int'], 'gmp_setbit' => ['void', 'num'=>'GMP|string|int', 'index'=>'int', 'value='=>'bool'], 'gmp_sign' => ['int', 'num'=>'GMP|string|int'], 'gmp_sqrt' => ['GMP', 'num'=>'GMP|string|int'], -'gmp_sqrtrem' => ['array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int'], +'gmp_sqrtrem' => ['strict-array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int'], 'gmp_strval' => ['numeric-string', 'num'=>'GMP|string|int', 'base='=>'int'], 'gmp_sub' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_testbit' => ['bool', 'num'=>'GMP|string|int', 'index'=>'int'], @@ -4377,7 +4377,7 @@ 'hexdec' => ['int|float', 'hex_string'=>'string'], 'highlight_file' => ['string|bool', 'filename'=>'string', 'return='=>'bool'], 'highlight_string' => ['string|bool', 'string'=>'string', 'return='=>'bool'], -'hrtime' => ['array{0:int,1:int}|false', 'as_number='=>'false'], +'hrtime' => ['strict-array{0:int,1:int}|false', 'as_number='=>'false'], 'hrtime\'1' => ['int|float|false', 'as_number='=>'true'], 'HRTime\PerformanceCounter::getElapsedTicks' => ['int'], 'HRTime\PerformanceCounter::getFrequency' => ['int'], @@ -5339,8 +5339,8 @@ 'image_type_to_extension' => ['string', 'image_type'=>'int', 'include_dot='=>'bool'], 'image_type_to_mime_type' => ['string', 'image_type'=>'int'], 'imageaffine' => ['false|GdImage', 'image'=>'GdImage', 'affine'=>'array', 'clip='=>'?array'], -'imageaffinematrixconcat' => ['array{0:float,1:float,2:float,3:float,4:float,5:float}|false', 'matrix1'=>'array', 'matrix2'=>'array'], -'imageaffinematrixget' => ['array{0:float,1:float,2:float,3:float,4:float,5:float}|false', 'type'=>'int', 'options'=>'array|float'], +'imageaffinematrixconcat' => ['strict-array{0:float,1:float,2:float,3:float,4:float,5:float}|false', 'matrix1'=>'array', 'matrix2'=>'array'], +'imageaffinematrixget' => ['strict-array{0:float,1:float,2:float,3:float,4:float,5:float}|false', 'type'=>'int', 'options'=>'array|float'], 'imagealphablending' => ['bool', 'image'=>'GdImage', 'enable'=>'bool'], 'imageantialias' => ['bool', 'image'=>'GdImage', 'enable'=>'bool'], 'imagearc' => ['bool', 'image'=>'GdImage', 'center_x'=>'int', 'center_y'=>'int', 'width'=>'int', 'height'=>'int', 'start_angle'=>'int', 'end_angle'=>'int', 'color'=>'int'], @@ -5483,9 +5483,9 @@ 'Imagick::colorMatrixImage' => ['void', 'color_matrix'=>'string'], 'Imagick::combineImages' => ['Imagick', 'channeltype'=>'int'], 'Imagick::commentImage' => ['bool', 'comment'=>'string'], -'Imagick::compareImageChannels' => ['array{Imagick, float}', 'image'=>'Imagick', 'channeltype'=>'int', 'metrictype'=>'int'], +'Imagick::compareImageChannels' => ['strict-array{Imagick, float}', 'image'=>'Imagick', 'channeltype'=>'int', 'metrictype'=>'int'], 'Imagick::compareImageLayers' => ['Imagick', 'method'=>'int'], -'Imagick::compareImages' => ['array{Imagick, float}', 'compare'=>'Imagick', 'metric'=>'int'], +'Imagick::compareImages' => ['strict-array{Imagick, float}', 'compare'=>'Imagick', 'metric'=>'int'], 'Imagick::compositeImage' => ['bool', 'composite_object'=>'Imagick', 'composite'=>'int', 'x'=>'int', 'y'=>'int', 'channel='=>'int'], 'Imagick::compositeImageGravity' => ['bool', 'Imagick'=>'Imagick', 'COMPOSITE_CONSTANT'=>'int', 'GRAVITY_CONSTANT'=>'int'], 'Imagick::contrastImage' => ['bool', 'sharpen'=>'bool'], @@ -5545,16 +5545,16 @@ 'Imagick::getImageAttribute' => ['string', 'key'=>'string'], 'Imagick::getImageBackgroundColor' => ['ImagickPixel'], 'Imagick::getImageBlob' => ['string'], -'Imagick::getImageBluePrimary' => ['array{x:float, y:float}'], +'Imagick::getImageBluePrimary' => ['strict-array{x:float, y:float}'], 'Imagick::getImageBorderColor' => ['ImagickPixel'], 'Imagick::getImageChannelDepth' => ['int', 'channel'=>'int'], 'Imagick::getImageChannelDistortion' => ['float', 'reference'=>'Imagick', 'channel'=>'int', 'metric'=>'int'], 'Imagick::getImageChannelDistortions' => ['float', 'reference'=>'Imagick', 'metric'=>'int', 'channel='=>'int'], -'Imagick::getImageChannelExtrema' => ['array{minima:int, maxima:int}', 'channel'=>'int'], -'Imagick::getImageChannelKurtosis' => ['array{kurtosis:float, skewness:float}', 'channel='=>'int'], -'Imagick::getImageChannelMean' => ['array{mean:float, standardDeviation:float}', 'channel'=>'int'], -'Imagick::getImageChannelRange' => ['array{minima:float, maxima:float}', 'channel'=>'int'], -'Imagick::getImageChannelStatistics' => ['array'], +'Imagick::getImageChannelExtrema' => ['strict-array{minima:int, maxima:int}', 'channel'=>'int'], +'Imagick::getImageChannelKurtosis' => ['strict-array{kurtosis:float, skewness:float}', 'channel='=>'int'], +'Imagick::getImageChannelMean' => ['strict-array{mean:float, standardDeviation:float}', 'channel'=>'int'], +'Imagick::getImageChannelRange' => ['strict-array{minima:float, maxima:float}', 'channel'=>'int'], +'Imagick::getImageChannelStatistics' => ['array'], 'Imagick::getImageClipMask' => ['Imagick'], 'Imagick::getImageColormapColor' => ['ImagickPixel', 'index'=>'int'], 'Imagick::getImageColors' => ['int'], @@ -5566,13 +5566,13 @@ 'Imagick::getImageDepth' => ['int'], 'Imagick::getImageDispose' => ['int'], 'Imagick::getImageDistortion' => ['float', 'reference'=>'magickwand', 'metric'=>'int'], -'Imagick::getImageExtrema' => ['array{min:int, max:int}'], +'Imagick::getImageExtrema' => ['strict-array{min:int, max:int}'], 'Imagick::getImageFilename' => ['string'], 'Imagick::getImageFormat' => ['string'], 'Imagick::getImageGamma' => ['float'], -'Imagick::getImageGeometry' => ['array{width:int, height:int}'], +'Imagick::getImageGeometry' => ['strict-array{width:int, height:int}'], 'Imagick::getImageGravity' => ['int'], -'Imagick::getImageGreenPrimary' => ['array{x:float, y:float}'], +'Imagick::getImageGreenPrimary' => ['strict-array{x:float, y:float}'], 'Imagick::getImageHeight' => ['int'], 'Imagick::getImageHistogram' => ['list'], 'Imagick::getImageIndex' => ['int'], @@ -5585,16 +5585,16 @@ 'Imagick::getImageMatteColor' => ['ImagickPixel'], 'Imagick::getImageMimeType' => ['string'], 'Imagick::getImageOrientation' => ['int'], -'Imagick::getImagePage' => ['array{width:int, height:int, x:int, y:int}'], +'Imagick::getImagePage' => ['strict-array{width:int, height:int, x:int, y:int}'], 'Imagick::getImagePixelColor' => ['ImagickPixel', 'x'=>'int', 'y'=>'int'], 'Imagick::getImageProfile' => ['string', 'name'=>'string'], 'Imagick::getImageProfiles' => ['array', 'pattern='=>'string', 'only_names='=>'bool'], 'Imagick::getImageProperties' => ['array', 'pattern='=>'string', 'only_names='=>'bool'], 'Imagick::getImageProperty' => ['string|false', 'name'=>'string'], -'Imagick::getImageRedPrimary' => ['array{x:float, y:float}'], +'Imagick::getImageRedPrimary' => ['strict-array{x:float, y:float}'], 'Imagick::getImageRegion' => ['Imagick', 'width'=>'int', 'height'=>'int', 'x'=>'int', 'y'=>'int'], 'Imagick::getImageRenderingIntent' => ['int'], -'Imagick::getImageResolution' => ['array{x:float, y:float}'], +'Imagick::getImageResolution' => ['strict-array{x:float, y:float}'], 'Imagick::getImagesBlob' => ['string'], 'Imagick::getImageScene' => ['int'], 'Imagick::getImageSignature' => ['string'], @@ -5604,28 +5604,28 @@ 'Imagick::getImageType' => ['int'], 'Imagick::getImageUnits' => ['int'], 'Imagick::getImageVirtualPixelMethod' => ['int'], -'Imagick::getImageWhitePoint' => ['array{x:float, y:float}'], +'Imagick::getImageWhitePoint' => ['strict-array{x:float, y:float}'], 'Imagick::getImageWidth' => ['int'], 'Imagick::getInterlaceScheme' => ['int'], 'Imagick::getIteratorIndex' => ['int'], 'Imagick::getNumberImages' => ['int'], 'Imagick::getOption' => ['string', 'key'=>'string'], 'Imagick::getPackageName' => ['string'], -'Imagick::getPage' => ['array{width:int, height:int, x:int, y:int}'], +'Imagick::getPage' => ['strict-array{width:int, height:int, x:int, y:int}'], 'Imagick::getPixelIterator' => ['ImagickPixelIterator'], 'Imagick::getPixelRegionIterator' => ['ImagickPixelIterator', 'x'=>'int', 'y'=>'int', 'columns'=>'int', 'rows'=>'int'], 'Imagick::getPointSize' => ['float'], 'Imagick::getQuantum' => ['int'], -'Imagick::getQuantumDepth' => ['array{quantumDepthLong:int, quantumDepthString:string}'], -'Imagick::getQuantumRange' => ['array{quantumRangeLong:int, quantumRangeString:string}'], +'Imagick::getQuantumDepth' => ['strict-array{quantumDepthLong:int, quantumDepthString:string}'], +'Imagick::getQuantumRange' => ['strict-array{quantumRangeLong:int, quantumRangeString:string}'], 'Imagick::getRegistry' => ['string|false', 'key'=>'string'], 'Imagick::getReleaseDate' => ['string'], 'Imagick::getResource' => ['int', 'type'=>'int'], 'Imagick::getResourceLimit' => ['int', 'type'=>'int'], 'Imagick::getSamplingFactors' => ['array'], -'Imagick::getSize' => ['array{columns:int, rows: int}'], +'Imagick::getSize' => ['strict-array{columns:int, rows: int}'], 'Imagick::getSizeOffset' => ['int'], -'Imagick::getVersion' => ['array{versionNumber: int, versionString:string}'], +'Imagick::getVersion' => ['strict-array{versionNumber: int, versionString:string}'], 'Imagick::haldClutImage' => ['bool', 'clut'=>'Imagick', 'channel='=>'int'], 'Imagick::hasNextImage' => ['bool'], 'Imagick::hasPreviousImage' => ['bool'], @@ -5966,13 +5966,13 @@ 'ImagickPixel::clear' => ['bool'], 'ImagickPixel::clone' => ['void'], 'ImagickPixel::destroy' => ['bool'], -'ImagickPixel::getColor' => ['array{r: int|float, g: int|float, b: int|float, a: int|float}', 'normalized='=>'0|1|2'], +'ImagickPixel::getColor' => ['strict-array{r: int|float, g: int|float, b: int|float, a: int|float}', 'normalized='=>'0|1|2'], 'ImagickPixel::getColorAsString' => ['string'], 'ImagickPixel::getColorCount' => ['int'], 'ImagickPixel::getColorQuantum' => ['mixed'], 'ImagickPixel::getColorValue' => ['float', 'color'=>'int'], 'ImagickPixel::getColorValueQuantum' => ['mixed'], -'ImagickPixel::getHSL' => ['array{hue: float, saturation: float, luminosity: float}'], +'ImagickPixel::getHSL' => ['strict-array{hue: float, saturation: float, luminosity: float}'], 'ImagickPixel::getIndex' => ['int'], 'ImagickPixel::isPixelSimilar' => ['bool', 'color'=>'ImagickPixel', 'fuzz'=>'float'], 'ImagickPixel::isPixelSimilarQuantum' => ['bool', 'color'=>'string', 'fuzz='=>'string'], @@ -6372,7 +6372,7 @@ 'IntlException::getLine' => ['int'], 'IntlException::getMessage' => ['string'], 'IntlException::getPrevious' => ['?Throwable'], -'IntlException::getTrace' => ['list\',args?:array}>'], +'IntlException::getTrace' => ['list\',args?:array}>'], 'IntlException::getTraceAsString' => ['string'], 'intlgregcal_create_instance' => ['IntlGregorianCalendar', 'timezoneOrYear='=>'mixed', 'localeOrMonth='=>'string'], 'intlgregcal_get_gregorian_change' => ['float', 'calendar'=>'IntlGregorianCalendar'], @@ -6512,7 +6512,7 @@ 'InvalidArgumentException::getLine' => ['int'], 'InvalidArgumentException::getMessage' => ['string'], 'InvalidArgumentException::getPrevious' => ['Throwable|InvalidArgumentException|null'], -'InvalidArgumentException::getTrace' => ['list\',args?:array}>'], +'InvalidArgumentException::getTrace' => ['list\',args?:array}>'], 'InvalidArgumentException::getTraceAsString' => ['string'], 'ip2long' => ['int|false', 'ip'=>'string'], 'iptcembed' => ['string|bool', 'iptc_data'=>'string', 'filename'=>'string', 'spool='=>'int'], @@ -6598,7 +6598,7 @@ 'JsonException::getLine' => ['int'], 'JsonException::getMessage' => ['string'], 'JsonException::getPrevious' => ['?Throwable'], -'JsonException::getTrace' => ['list\',args?:array}>'], +'JsonException::getTrace' => ['list\',args?:array}>'], 'JsonException::getTraceAsString' => ['string'], 'JsonIncrementalParser::__construct' => ['void', 'depth'=>'', 'options'=>''], 'JsonIncrementalParser::get' => ['', 'options'=>''], @@ -6824,7 +6824,7 @@ 'LengthException::getLine' => ['int'], 'LengthException::getMessage' => ['string'], 'LengthException::getPrevious' => ['Throwable|LengthException|null'], -'LengthException::getTrace' => ['list\',args?:array}>'], +'LengthException::getTrace' => ['list\',args?:array}>'], 'LengthException::getTraceAsString' => ['string'], 'LevelDB::__construct' => ['void', 'name'=>'string', 'options='=>'array', 'read_options='=>'array', 'write_options='=>'array'], 'LevelDB::close' => [''], @@ -6936,10 +6936,10 @@ 'LogicException::getLine' => ['int'], 'LogicException::getMessage' => ['string'], 'LogicException::getPrevious' => ['Throwable|LogicException|null'], -'LogicException::getTrace' => ['list\',args?:array}>'], +'LogicException::getTrace' => ['list\',args?:array}>'], 'LogicException::getTraceAsString' => ['string'], 'long2ip' => ['string', 'ip'=>'string|int'], -'lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], +'lstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'ltrim' => ['string', 'string'=>'string', 'characters='=>'string'], 'Lua::__call' => ['mixed', 'lua_func'=>'callable', 'args='=>'array', 'use_self='=>'int'], 'Lua::__construct' => ['void', 'lua_script_file'=>'string'], @@ -7647,7 +7647,7 @@ 'MongoCursorException::getLine' => ['int'], 'MongoCursorException::getMessage' => ['string'], 'MongoCursorException::getPrevious' => ['Exception|Throwable'], -'MongoCursorException::getTrace' => ['list\',args?:array}>'], +'MongoCursorException::getTrace' => ['list\',args?:array}>'], 'MongoCursorException::getTraceAsString' => ['string'], 'MongoCursorInterface::__construct' => ['void'], 'MongoCursorInterface::batchSize' => ['MongoCursorInterface', 'batchSize'=>'int'], @@ -8015,7 +8015,7 @@ 'MongoException::getLine' => ['int'], 'MongoException::getMessage' => ['string'], 'MongoException::getPrevious' => ['Exception|Throwable'], -'MongoException::getTrace' => ['list\',args?:array}>'], +'MongoException::getTrace' => ['list\',args?:array}>'], 'MongoException::getTraceAsString' => ['string'], 'MongoGridFS::__construct' => ['void', 'db'=>'MongoDB', 'prefix='=>'string', 'chunks='=>'mixed'], 'MongoGridFS::__get' => ['MongoCollection', 'name'=>'string'], @@ -8126,7 +8126,7 @@ 'MongoResultException::getLine' => ['int'], 'MongoResultException::getMessage' => ['string'], 'MongoResultException::getPrevious' => ['Exception|Throwable'], -'MongoResultException::getTrace' => ['list\',args?:array}>'], +'MongoResultException::getTrace' => ['list\',args?:array}>'], 'MongoResultException::getTraceAsString' => ['string'], 'MongoTimestamp::__construct' => ['void', 'second='=>'int', 'inc='=>'int'], 'MongoTimestamp::__toString' => ['string'], @@ -8146,7 +8146,7 @@ 'MongoWriteConcernException::getLine' => ['int'], 'MongoWriteConcernException::getMessage' => ['string'], 'MongoWriteConcernException::getPrevious' => ['Exception|Throwable'], -'MongoWriteConcernException::getTrace' => ['list\',args?:array}>'], +'MongoWriteConcernException::getTrace' => ['list\',args?:array}>'], 'MongoWriteConcernException::getTraceAsString' => ['string'], 'monitor_custom_event' => ['void', 'class'=>'string', 'text'=>'string', 'severe='=>'int', 'user_data='=>'mixed'], 'monitor_httperror_event' => ['void', 'error_code'=>'int', 'url'=>'string', 'severe='=>'int'], @@ -9424,7 +9424,7 @@ 'OutOfBoundsException::getLine' => ['int'], 'OutOfBoundsException::getMessage' => ['string'], 'OutOfBoundsException::getPrevious' => ['Throwable|OutOfBoundsException|null'], -'OutOfBoundsException::getTrace' => ['list\',args?:array}>'], +'OutOfBoundsException::getTrace' => ['list\',args?:array}>'], 'OutOfBoundsException::getTraceAsString' => ['string'], 'OutOfRangeException::__clone' => ['void'], 'OutOfRangeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?OutOfRangeException'], @@ -9434,7 +9434,7 @@ 'OutOfRangeException::getLine' => ['int'], 'OutOfRangeException::getMessage' => ['string'], 'OutOfRangeException::getPrevious' => ['Throwable|OutOfRangeException|null'], -'OutOfRangeException::getTrace' => ['list\',args?:array}>'], +'OutOfRangeException::getTrace' => ['list\',args?:array}>'], 'OutOfRangeException::getTraceAsString' => ['string'], 'output_add_rewrite_var' => ['bool', 'name'=>'string', 'value'=>'string'], 'output_cache_disable' => ['void'], @@ -9461,7 +9461,7 @@ 'OverflowException::getLine' => ['int'], 'OverflowException::getMessage' => ['string'], 'OverflowException::getPrevious' => ['Throwable|OverflowException|null'], -'OverflowException::getTrace' => ['list\',args?:array}>'], +'OverflowException::getTrace' => ['list\',args?:array}>'], 'OverflowException::getTraceAsString' => ['string'], 'overload' => ['', 'class_name'=>'string'], 'override_function' => ['bool', 'function_name'=>'string', 'function_args'=>'string', 'function_code'=>'string'], @@ -9553,7 +9553,7 @@ 'ParseError::getLine' => ['int'], 'ParseError::getMessage' => ['string'], 'ParseError::getPrevious' => ['Throwable|ParseError|null'], -'ParseError::getTrace' => ['list\',args?:array}>'], +'ParseError::getTrace' => ['list\',args?:array}>'], 'ParseError::getTraceAsString' => ['string'], 'parsekit_compile_file' => ['array', 'filename'=>'string', 'errors='=>'array', 'options='=>'int'], 'parsekit_compile_string' => ['array', 'phpcode'=>'string', 'errors='=>'array', 'options='=>'int'], @@ -9905,7 +9905,7 @@ 'PDO::commit' => ['bool'], 'PDO::cubrid_schema' => ['array', 'schema_type'=>'int', 'table_name='=>'string', 'col_name='=>'string'], 'PDO::errorCode' => ['?string'], -'PDO::errorInfo' => ['array{0: ?string, 1: ?int, 2: ?string, 3?: mixed, 4?: mixed}'], +'PDO::errorInfo' => ['strict-array{0: ?string, 1: ?int, 2: ?string, 3?: mixed, 4?: mixed}'], 'PDO::exec' => ['int|false', 'query'=>'string'], 'PDO::getAttribute' => ['', 'attribute'=>'int'], 'PDO::getAvailableDrivers' => ['array'], @@ -9915,7 +9915,7 @@ 'PDO::pgsqlCopyFromFile' => ['bool', 'table_name'=>'string', 'filename'=>'string', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'], 'PDO::pgsqlCopyToArray' => ['array', 'table_name'=>'string', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'], 'PDO::pgsqlCopyToFile' => ['bool', 'table_name'=>'string', 'filename'=>'string', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'], -'PDO::pgsqlGetNotify' => ['array{message:string,pid:int,payload?:string}|false', 'result_type='=>'PDO::FETCH_*', 'ms_timeout='=>'int'], +'PDO::pgsqlGetNotify' => ['strict-array{message:string,pid:int,payload?:string}|false', 'result_type='=>'PDO::FETCH_*', 'ms_timeout='=>'int'], 'PDO::pgsqlGetPid' => ['int'], 'PDO::pgsqlLOBCreate' => ['string'], 'PDO::pgsqlLOBOpen' => ['resource', 'oid'=>'string', 'mode='=>'string'], @@ -9937,7 +9937,7 @@ 'PDOException::getLine' => ['int'], 'PDOException::getMessage' => ['string'], 'PDOException::getPrevious' => ['?Throwable'], -'PDOException::getTrace' => ['list\',args?:array}>'], +'PDOException::getTrace' => ['list\',args?:array}>'], 'PDOException::getTraceAsString' => ['string'], 'PDOStatement::__sleep' => ['list'], 'PDOStatement::__wakeup' => ['void'], @@ -9948,7 +9948,7 @@ 'PDOStatement::columnCount' => ['int'], 'PDOStatement::debugDumpParams' => ['bool|null'], 'PDOStatement::errorCode' => ['string|null'], -'PDOStatement::errorInfo' => ['array{0: ?string, 1: ?int, 2: ?string, 3?: mixed, 4?: mixed}'], +'PDOStatement::errorInfo' => ['strict-array{0: ?string, 1: ?int, 2: ?string, 3?: mixed, 4?: mixed}'], 'PDOStatement::execute' => ['bool', 'params='=>'?array'], 'PDOStatement::fetch' => ['mixed', 'mode='=>'int', 'cursorOrientation='=>'int', 'cursorOffset='=>'int'], 'PDOStatement::fetchAll' => ['array', 'mode='=>'int', '...args='=>'mixed'], @@ -10097,7 +10097,7 @@ 'Phar::getMetadata' => ['mixed', 'unserializeOptions='=>'array'], 'Phar::getModified' => ['bool'], 'Phar::getPath' => ['string'], -'Phar::getSignature' => ['array{hash:string, hash_type:string}'], +'Phar::getSignature' => ['strict-array{hash:string, hash_type:string}'], 'Phar::getStub' => ['string'], 'Phar::getSupportedCompression' => ['array'], 'Phar::getSupportedSignatures' => ['array'], @@ -10278,17 +10278,17 @@ 'posix_getegid' => ['int'], 'posix_geteuid' => ['int'], 'posix_getgid' => ['int'], -'posix_getgrgid' => ['array{name: string, passwd: string, gid: int, members: list}|false', 'group_id'=>'int'], -'posix_getgrnam' => ['array{name: string, passwd: string, gid: int, members: list}|false', 'name'=>'string'], +'posix_getgrgid' => ['strict-array{name: string, passwd: string, gid: int, members: list}|false', 'group_id'=>'int'], +'posix_getgrnam' => ['strict-array{name: string, passwd: string, gid: int, members: list}|false', 'name'=>'string'], 'posix_getgroups' => ['list|false'], 'posix_getlogin' => ['string|false'], 'posix_getpgid' => ['int|false', 'process_id'=>'int'], 'posix_getpgrp' => ['int'], 'posix_getpid' => ['int'], 'posix_getppid' => ['int'], -'posix_getpwnam' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'username'=>'string'], -'posix_getpwuid' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'user_id'=>'int'], -'posix_getrlimit' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'], +'posix_getpwnam' => ['strict-array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'username'=>'string'], +'posix_getpwuid' => ['strict-array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'user_id'=>'int'], +'posix_getrlimit' => ['strict-array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'], 'posix_getsid' => ['int|false', 'process_id'=>'int'], 'posix_getuid' => ['int'], 'posix_initgroups' => ['bool', 'username'=>'string', 'group_id'=>'int'], @@ -10304,9 +10304,9 @@ 'posix_setsid' => ['int'], 'posix_setuid' => ['bool', 'user_id'=>'int'], 'posix_strerror' => ['string', 'error_code'=>'int'], -'posix_times' => ['array{ticks: int, utime: int, stime: int, cutime: int, cstime: int}|false'], +'posix_times' => ['strict-array{ticks: int, utime: int, stime: int, cutime: int, cstime: int}|false'], 'posix_ttyname' => ['string|false', 'file_descriptor'=>'resource|int'], -'posix_uname' => ['array{sysname: string, nodename: string, release: string, version: string, machine: string, domainname: string}|false'], +'posix_uname' => ['strict-array{sysname: string, nodename: string, release: string, version: string, machine: string, domainname: string}|false'], 'Postal\Expand::expand_address' => ['string[]', 'address'=>'string', 'options='=>'array'], 'Postal\Parser::parse_address' => ['array', 'address'=>'string', 'options='=>'array'], 'pow' => ['float|int', 'num'=>'int|float', 'exponent'=>'int|float'], @@ -10329,7 +10329,7 @@ 'print_r\'1' => ['true', 'value'=>'mixed', 'return='=>'bool'], 'printf' => ['int', 'format'=>'string', '...values='=>'string|int|float'], 'proc_close' => ['int', 'process'=>'resource'], -'proc_get_status' => ['array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}', 'process'=>'resource'], +'proc_get_status' => ['strict-array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}', 'process'=>'resource'], 'proc_nice' => ['bool', 'priority'=>'int'], 'proc_open' => ['resource|false', 'command'=>'string|array', 'descriptor_spec'=>'array', '&pipes'=>'resource[]', 'cwd='=>'?string', 'env_vars='=>'?array', 'options='=>'?array'], 'proc_terminate' => ['bool', 'process'=>'resource', 'signal='=>'int'], @@ -10556,7 +10556,7 @@ 'RangeException::getLine' => ['int'], 'RangeException::getMessage' => ['string'], 'RangeException::getPrevious' => ['Throwable|RangeException|null'], -'RangeException::getTrace' => ['list\',args?:array}>'], +'RangeException::getTrace' => ['list\',args?:array}>'], 'RangeException::getTraceAsString' => ['string'], 'rar_allow_broken_set' => ['bool', 'rarfile'=>'RarArchive', 'allow_broken'=>'bool'], 'rar_broken_is' => ['bool', 'rarfile'=>'rararchive'], @@ -10595,7 +10595,7 @@ 'RarException::getLine' => ['int'], 'RarException::getMessage' => ['string'], 'RarException::getPrevious' => ['Exception|Throwable'], -'RarException::getTrace' => ['list\',args?:array}>'], +'RarException::getTrace' => ['list\',args?:array}>'], 'RarException::getTraceAsString' => ['string'], 'RarException::isUsingExceptions' => ['bool'], 'RarException::setUsingExceptions' => ['RarEntry', 'using_exceptions'=>'bool'], @@ -10945,7 +10945,7 @@ 'Redis::geoAdd' => ['int', 'key'=>'string', 'longitude'=>'float', 'latitude'=>'float', 'member'=>'string', '...other_triples='=>'string|int|float'], 'Redis::geoDist' => ['float', 'key'=>'string', 'member1'=>'string', 'member2'=>'string', 'unit='=>'string'], 'Redis::geoHash' => ['array', 'key'=>'string', 'member'=>'string', '...other_members='=>'string'], -'Redis::geoPos' => ['array', 'key'=>'string', 'member'=>'string', '...members='=>'string'], +'Redis::geoPos' => ['array', 'key'=>'string', 'member'=>'string', '...members='=>'string'], 'Redis::geoRadius' => ['array|int', 'key'=>'string', 'longitude'=>'float', 'latitude'=>'float', 'radius'=>'float', 'unit'=>'float', 'options='=>'array'], 'Redis::geoRadiusByMember' => ['array|int', 'key'=>'string', 'member'=>'string', 'radius'=>'float', 'units'=>'string', 'options='=>'array'], 'Redis::get' => ['string|false', 'key'=>'string'], @@ -11208,7 +11208,7 @@ 'RedisCluster::geoAdd' => ['int', 'key'=>'string', 'longitude'=>'float', 'latitude'=>'float', 'member'=>'string', '...other_members='=>'float|string'], 'RedisCluster::geoDist' => ['', 'key'=>'string', 'member1'=>'string', 'member2'=>'string', 'unit='=>'string'], 'RedisCluster::geohash' => ['array', 'key'=>'string', 'member'=>'string', '...other_members='=>'string'], -'RedisCluster::geopos' => ['array', 'key'=>'string', 'member'=>'string', '...other_members='=>'string'], +'RedisCluster::geopos' => ['array', 'key'=>'string', 'member'=>'string', '...other_members='=>'string'], 'RedisCluster::geoRadius' => ['', 'key'=>'string', 'longitude'=>'float', 'latitude'=>'float', 'radius'=>'float', 'radiusUnit'=>'string', 'options='=>'array'], 'RedisCluster::geoRadiusByMember' => ['string[]', 'key'=>'string', 'member'=>'string', 'radius'=>'float', 'radiusUnit'=>'string', 'options='=>'array'], 'RedisCluster::get' => ['string|false', 'key'=>'string'], @@ -11805,7 +11805,7 @@ 'RuntimeException::getLine' => ['int'], 'RuntimeException::getMessage' => ['string'], 'RuntimeException::getPrevious' => ['Throwable|RuntimeException|null'], -'RuntimeException::getTrace' => ['list\',args?:array}>'], +'RuntimeException::getTrace' => ['list\',args?:array}>'], 'RuntimeException::getTraceAsString' => ['string'], 'SAMConnection::commit' => ['bool'], 'SAMConnection::connect' => ['bool', 'protocol'=>'string', 'properties='=>'array'], @@ -12307,7 +12307,7 @@ 'SoapFault::getLine' => ['int'], 'SoapFault::getMessage' => ['string'], 'SoapFault::getPrevious' => ['?Exception|?Throwable'], -'SoapFault::getTrace' => ['list\',args?:array}>'], +'SoapFault::getTrace' => ['list\',args?:array}>'], 'SoapFault::getTraceAsString' => ['string'], 'SoapFault::SoapFault' => ['object', 'faultcode'=>'string', 'faultstring'=>'string', 'faultactor='=>'?string', 'detail='=>'?mixed', 'faultname='=>'?string', 'headerfault='=>'?mixed'], 'SoapHeader::__construct' => ['void', 'namespace'=>'string', 'name'=>'string', 'data='=>'mixed', 'mustunderstand='=>'bool', 'actor='=>'string'], @@ -12487,7 +12487,7 @@ 'SolrClientException::getLine' => ['int'], 'SolrClientException::getMessage' => ['string'], 'SolrClientException::getPrevious' => ['?Exception|?Throwable'], -'SolrClientException::getTrace' => ['list\',args?:array}>'], +'SolrClientException::getTrace' => ['list\',args?:array}>'], 'SolrClientException::getTraceAsString' => ['string'], 'SolrCollapseFunction::__construct' => ['void', 'field'=>'string'], 'SolrCollapseFunction::__toString' => ['string'], @@ -12779,7 +12779,7 @@ 'SolrException::getLine' => ['int'], 'SolrException::getMessage' => ['string'], 'SolrException::getPrevious' => ['Exception|Throwable'], -'SolrException::getTrace' => ['list\',args?:array}>'], +'SolrException::getTrace' => ['list\',args?:array}>'], 'SolrException::getTraceAsString' => ['string'], 'SolrGenericResponse::__construct' => ['void'], 'SolrGenericResponse::__destruct' => ['void'], @@ -12804,7 +12804,7 @@ 'SolrIllegalArgumentException::getLine' => ['int'], 'SolrIllegalArgumentException::getMessage' => ['string'], 'SolrIllegalArgumentException::getPrevious' => ['Exception|Throwable'], -'SolrIllegalArgumentException::getTrace' => ['list\',args?:array}>'], +'SolrIllegalArgumentException::getTrace' => ['list\',args?:array}>'], 'SolrIllegalArgumentException::getTraceAsString' => ['string'], 'SolrIllegalOperationException::__clone' => ['void'], 'SolrIllegalOperationException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Exception|?Throwable'], @@ -12816,7 +12816,7 @@ 'SolrIllegalOperationException::getLine' => ['int'], 'SolrIllegalOperationException::getMessage' => ['string'], 'SolrIllegalOperationException::getPrevious' => ['Exception|Throwable'], -'SolrIllegalOperationException::getTrace' => ['list\',args?:array}>'], +'SolrIllegalOperationException::getTrace' => ['list\',args?:array}>'], 'SolrIllegalOperationException::getTraceAsString' => ['string'], 'SolrInputDocument::__clone' => ['void'], 'SolrInputDocument::__construct' => ['void'], @@ -13124,7 +13124,7 @@ 'SolrServerException::getLine' => ['int'], 'SolrServerException::getMessage' => ['string'], 'SolrServerException::getPrevious' => ['Exception|Throwable'], -'SolrServerException::getTrace' => ['list\',args?:array}>'], +'SolrServerException::getTrace' => ['list\',args?:array}>'], 'SolrServerException::getTraceAsString' => ['string'], 'SolrUpdateResponse::__construct' => ['void'], 'SolrUpdateResponse::__destruct' => ['void'], @@ -13251,7 +13251,7 @@ 'SplFileObject::eof' => ['bool'], 'SplFileObject::fflush' => ['bool'], 'SplFileObject::fgetc' => ['string|false'], -'SplFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'SplFileObject::fgetcsv' => ['list|strict-array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplFileObject::fgets' => ['string|false'], 'SplFileObject::flock' => ['bool', 'operation'=>'int', '&w_wouldblock='=>'int'], 'SplFileObject::fpassthru' => ['int|false'], @@ -13259,7 +13259,7 @@ 'SplFileObject::fread' => ['string|false', 'length'=>'int'], 'SplFileObject::fscanf' => ['array|int', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'SplFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], -'SplFileObject::fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], +'SplFileObject::fstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], 'SplFileObject::ftell' => ['int|false'], 'SplFileObject::ftruncate' => ['bool', 'size'=>'int'], 'SplFileObject::fwrite' => ['int', 'string'=>'string', 'length='=>'int'], @@ -13443,7 +13443,7 @@ 'SplTempFileObject::eof' => ['bool'], 'SplTempFileObject::fflush' => ['bool'], 'SplTempFileObject::fgetc' => ['false|string'], -'SplTempFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], +'SplTempFileObject::fgetcsv' => ['list|strict-array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplTempFileObject::fgets' => ['string'], 'SplTempFileObject::fgetss' => ['string', 'allowable_tags='=>'string'], 'SplTempFileObject::flock' => ['bool', 'operation'=>'int', '&wouldblock='=>'int'], @@ -13452,7 +13452,7 @@ 'SplTempFileObject::fread' => ['false|string', 'length'=>'int'], 'SplTempFileObject::fscanf' => ['bool', 'format'=>'string', '&...w_vars='=>'array|array|array'], 'SplTempFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], -'SplTempFileObject::fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], +'SplTempFileObject::fstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], 'SplTempFileObject::ftell' => ['int'], 'SplTempFileObject::ftruncate' => ['bool', 'size'=>'int'], 'SplTempFileObject::fwrite' => ['int', 'string'=>'string', 'length='=>'int'], @@ -13605,7 +13605,7 @@ 'SQLiteException::getLine' => ['int'], 'SQLiteException::getMessage' => ['string'], 'SQLiteException::getPrevious' => ['RuntimeException|Throwable|null'], -'SQLiteException::getTrace' => ['list\',args?:array}>'], +'SQLiteException::getTrace' => ['list\',args?:array}>'], 'SQLiteException::getTraceAsString' => ['string'], 'SQLiteResult::__construct' => ['void'], 'SQLiteResult::column' => ['mixed', 'index_or_name'=>'', 'decode_binary='=>'bool'], @@ -13689,18 +13689,18 @@ 'ssh2_scp_send' => ['bool', 'session'=>'resource', 'local_file'=>'string', 'remote_file'=>'string', 'create_mode='=>'int'], 'ssh2_sftp' => ['resource|false', 'session'=>'resource'], 'ssh2_sftp_chmod' => ['bool', 'sftp'=>'resource', 'filename'=>'string', 'mode'=>'int'], -'ssh2_sftp_lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], +'ssh2_sftp_lstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], 'ssh2_sftp_mkdir' => ['bool', 'sftp'=>'resource', 'dirname'=>'string', 'mode='=>'int', 'recursive='=>'bool'], 'ssh2_sftp_readlink' => ['string|false', 'sftp'=>'resource', 'link'=>'string'], 'ssh2_sftp_realpath' => ['string|false', 'sftp'=>'resource', 'filename'=>'string'], 'ssh2_sftp_rename' => ['bool', 'sftp'=>'resource', 'from'=>'string', 'to'=>'string'], 'ssh2_sftp_rmdir' => ['bool', 'sftp'=>'resource', 'dirname'=>'string'], -'ssh2_sftp_stat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], +'ssh2_sftp_stat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], 'ssh2_sftp_symlink' => ['bool', 'sftp'=>'resource', 'target'=>'string', 'link'=>'string'], 'ssh2_sftp_unlink' => ['bool', 'sftp'=>'resource', 'filename'=>'string'], 'ssh2_shell' => ['resource|false', 'session'=>'resource', 'term_type='=>'string', 'env='=>'array', 'width='=>'int', 'height='=>'int', 'width_height_type='=>'int'], 'ssh2_tunnel' => ['resource|false', 'session'=>'resource', 'host'=>'string', 'port'=>'int'], -'stat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], +'stat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'stats_absolute_deviation' => ['float', 'a'=>'array'], 'stats_cdf_beta' => ['float', 'par1'=>'float', 'par2'=>'float', 'par3'=>'float', 'which'=>'int'], 'stats_cdf_binomial' => ['float', 'par1'=>'float', 'par2'=>'float', 'par3'=>'float', 'which'=>'int'], @@ -13847,7 +13847,7 @@ 'stream_get_contents' => ['string|false', 'stream'=>'resource', 'length='=>'int', 'offset='=>'int'], 'stream_get_filters' => ['array'], 'stream_get_line' => ['string|false', 'stream'=>'resource', 'length'=>'int', 'ending='=>'string'], -'stream_get_meta_data' => ['array{timed_out:bool,blocked:bool,eof:bool,unread_bytes:int,stream_type:string,wrapper_type:string,wrapper_data:mixed,mode:string,seekable:bool,uri:string,mediatype:string,crypto?:array{protocol:string,cipher_name:string,cipher_bits:int,cipher_version:string}}', 'stream'=>'resource'], +'stream_get_meta_data' => ['strict-array{timed_out:bool,blocked:bool,eof:bool,unread_bytes:int,stream_type:string,wrapper_type:string,wrapper_data:mixed,mode:string,seekable:bool,uri:string,mediatype:string,crypto?:strict-array{protocol:string,cipher_name:string,cipher_bits:int,cipher_version:string}}', 'stream'=>'resource'], 'stream_get_transports' => ['list'], 'stream_get_wrappers' => ['list'], 'stream_is_local' => ['bool', 'stream'=>'resource|string'], @@ -14604,7 +14604,7 @@ 'Throwable::getLine' => ['int'], 'Throwable::getMessage' => ['string'], 'Throwable::getPrevious' => ['?Throwable'], -'Throwable::getTrace' => ['list\',args?:array}>'], +'Throwable::getTrace' => ['list\',args?:array}>'], 'Throwable::getTraceAsString' => ['string'], 'tidy::__construct' => ['void', 'filename='=>'string', 'config='=>'', 'encoding='=>'string', 'useIncludePath='=>'bool'], 'tidy::body' => ['tidyNode'], @@ -14666,19 +14666,19 @@ 'tidyNode::isPhp' => ['bool'], 'tidyNode::isText' => ['bool'], 'time' => ['positive-int'], -'time_nanosleep' => ['array{0:0|positive-int,1:0|positive-int}|bool', 'seconds'=>'positive-int', 'nanoseconds'=>'positive-int'], +'time_nanosleep' => ['strict-array{0:0|positive-int,1:0|positive-int}|bool', 'seconds'=>'positive-int', 'nanoseconds'=>'positive-int'], 'time_sleep_until' => ['bool', 'timestamp'=>'float'], -'timezone_abbreviations_list' => ['array>|false'], +'timezone_abbreviations_list' => ['array>|false'], 'timezone_identifiers_list' => ['list', 'timezoneGroup='=>'int', 'countryCode='=>'?string'], 'timezone_location_get' => ['array|false', 'object'=>'DateTimeZone'], 'timezone_name_from_abbr' => ['string|false', 'abbr'=>'string', 'utcOffset='=>'int', 'isDST='=>'int'], 'timezone_name_get' => ['string', 'object'=>'DateTimeZone'], 'timezone_offset_get' => ['int|false', 'object'=>'DateTimeZone', 'datetime'=>'DateTimeInterface'], 'timezone_open' => ['DateTimeZone|false', 'timezone'=>'string'], -'timezone_transitions_get' => ['list|false', 'object'=>'DateTimeZone', 'timestampBegin='=>'int', 'timestampEnd='=>'int'], +'timezone_transitions_get' => ['list|false', 'object'=>'DateTimeZone', 'timestampBegin='=>'int', 'timestampEnd='=>'int'], 'timezone_version_get' => ['string'], 'tmpfile' => ['resource|false'], -'token_get_all' => ['list', 'code'=>'string', 'flags='=>'int'], +'token_get_all' => ['list', 'code'=>'string', 'flags='=>'int'], 'token_name' => ['string', 'id'=>'int'], 'TokyoTyrant::__construct' => ['void', 'host='=>'string', 'port='=>'int', 'options='=>'array'], 'TokyoTyrant::add' => ['int|float', 'key'=>'string', 'increment'=>'float', 'type='=>'int'], @@ -14924,7 +14924,7 @@ 'TypeError::getLine' => ['int'], 'TypeError::getMessage' => ['string'], 'TypeError::getPrevious' => ['Throwable|TypeError|null'], -'TypeError::getTrace' => ['list\',args?:array}>'], +'TypeError::getTrace' => ['list\',args?:array}>'], 'TypeError::getTraceAsString' => ['string'], 'uasort' => ['bool', '&rw_array'=>'array', 'callback'=>'callable(mixed,mixed):int'], 'ucfirst' => ['string', 'string'=>'string'], @@ -15147,7 +15147,7 @@ 'UnderflowException::getLine' => ['int'], 'UnderflowException::getMessage' => ['string'], 'UnderflowException::getPrevious' => ['Throwable|UnderflowException|null'], -'UnderflowException::getTrace' => ['list\',args?:array}>'], +'UnderflowException::getTrace' => ['list\',args?:array}>'], 'UnderflowException::getTraceAsString' => ['string'], 'UnexpectedValueException::__clone' => ['void'], 'UnexpectedValueException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?UnexpectedValueException'], @@ -15157,7 +15157,7 @@ 'UnexpectedValueException::getLine' => ['int'], 'UnexpectedValueException::getMessage' => ['string'], 'UnexpectedValueException::getPrevious' => ['Throwable|UnexpectedValueException|null'], -'UnexpectedValueException::getTrace' => ['list\',args?:array}>'], +'UnexpectedValueException::getTrace' => ['list\',args?:array}>'], 'UnexpectedValueException::getTraceAsString' => ['string'], 'uniqid' => ['non-empty-string', 'prefix='=>'string', 'more_entropy='=>'bool'], 'unixtojd' => ['int', 'timestamp='=>'int'], @@ -15250,7 +15250,7 @@ 'V8JsScriptException::getLine' => ['int'], 'V8JsScriptException::getMessage' => ['string'], 'V8JsScriptException::getPrevious' => ['Exception|Throwable'], -'V8JsScriptException::getTrace' => ['list\',args?:array}>'], +'V8JsScriptException::getTrace' => ['list\',args?:array}>'], 'V8JsScriptException::getTraceAsString' => ['string'], 'var_dump' => ['void', 'value'=>'mixed', '...values='=>'mixed'], 'var_export' => ['?string', 'value'=>'mixed', 'return='=>'bool'], @@ -16600,7 +16600,7 @@ 'Yar_Client_Exception::getLine' => ['int'], 'Yar_Client_Exception::getMessage' => ['string'], 'Yar_Client_Exception::getPrevious' => ['?Exception|?Throwable'], -'Yar_Client_Exception::getTrace' => ['list\',args?:array}>'], +'Yar_Client_Exception::getTrace' => ['list\',args?:array}>'], 'Yar_Client_Exception::getTraceAsString' => ['string'], 'Yar_Client_Exception::getType' => ['string'], 'Yar_Concurrent_Client::call' => ['int', 'uri'=>'string', 'method'=>'string', 'parameters'=>'array', 'callback='=>'callable'], @@ -16617,7 +16617,7 @@ 'Yar_Server_Exception::getLine' => ['int'], 'Yar_Server_Exception::getMessage' => ['string'], 'Yar_Server_Exception::getPrevious' => ['?Exception|?Throwable'], -'Yar_Server_Exception::getTrace' => ['list\',args?:array}>'], +'Yar_Server_Exception::getTrace' => ['list\',args?:array}>'], 'Yar_Server_Exception::getTraceAsString' => ['string'], 'Yar_Server_Exception::getType' => ['string'], 'yaz_addinfo' => ['string', 'id'=>'resource'], diff --git a/dictionaries/CallMap_73_delta.php b/dictionaries/CallMap_73_delta.php index 91002d47056..eba92b81ef3 100644 --- a/dictionaries/CallMap_73_delta.php +++ b/dictionaries/CallMap_73_delta.php @@ -26,18 +26,18 @@ 'JsonException::getLine' => ['int'], 'JsonException::getMessage' => ['string'], 'JsonException::getPrevious' => ['?Throwable'], - 'JsonException::getTrace' => ['list\',args?:array}>'], + 'JsonException::getTrace' => ['list\',args?:array}>'], 'JsonException::getTraceAsString' => ['string'], 'SplPriorityQueue::isCorrupted' => ['bool'], 'array_key_first' => ['int|string|null', 'array'=>'array'], 'array_key_last' => ['int|string|null', 'array'=>'array'], 'fpm_get_status' => ['array|false'], - 'gc_status' => ['array{runs:int,collected:int,threshold:int,roots:int}'], + 'gc_status' => ['strict-array{runs:int,collected:int,threshold:int,roots:int}'], 'gmp_binomial' => ['GMP|false', 'n'=>'GMP|string|int', 'k'=>'int'], 'gmp_kronecker' => ['int', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_lcm' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_perfect_power' => ['bool', 'num'=>'GMP|string|int'], - 'hrtime' => ['array{0:int,1:int}|false', 'as_number='=>'false'], + 'hrtime' => ['strict-array{0:int,1:int}|false', 'as_number='=>'false'], 'hrtime\'1' => ['int|float|false', 'as_number='=>'true'], 'is_countable' => ['bool', 'value'=>'mixed'], 'net_get_interfaces' => ['array>|false'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index 815d07edb21..b60e895b99b 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -1210,8 +1210,8 @@ 'new' => ['string', 'password'=>'string', 'algo'=>'int|string|null', 'options='=>'array'], ], 'proc_get_status' => [ - 'old' => ['array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}|false', 'process'=>'resource'], - 'new' => ['array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}', 'process'=>'resource'], + 'old' => ['strict-array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}|false', 'process'=>'resource'], + 'new' => ['strict-array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}', 'process'=>'resource'], ], 'session_set_cookie_params' => [ 'old' => ['bool', 'lifetime'=>'int', 'path='=>'string', 'domain='=>'string', 'secure='=>'bool', 'httponly='=>'bool'], @@ -1677,7 +1677,7 @@ 'ReflectionType::isBuiltin' => ['bool'], 'SplFileObject::fgetss' => ['string|false', 'allowable_tags='=>'string'], 'create_function' => ['string', 'args'=>'string', 'code'=>'string'], - 'each' => ['array{0:int|string,key:int|string,1:mixed,value:mixed}', '&r_arr'=>'array'], + 'each' => ['strict-array{0:int|string,key:int|string,1:mixed,value:mixed}', '&r_arr'=>'array'], 'gmp_random' => ['GMP', 'limiter='=>'int'], 'gzgetss' => ['string|false', 'zp'=>'resource', 'length'=>'int', 'allowable_tags='=>'string'], 'image2wbmp' => ['bool', 'im'=>'resource', 'filename='=>'?string', 'threshold='=>'int'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 97d739fa4d9..45445da2944 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -196,7 +196,7 @@ 'ArgumentCountError::getLine' => ['int'], 'ArgumentCountError::getMessage' => ['string'], 'ArgumentCountError::getPrevious' => ['?Throwable'], - 'ArgumentCountError::getTrace' => ['list\',args?:array}>'], + 'ArgumentCountError::getTrace' => ['list\',args?:array}>'], 'ArgumentCountError::getTraceAsString' => ['string'], 'ArithmeticError::__clone' => ['void'], 'ArithmeticError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?Error'], @@ -207,7 +207,7 @@ 'ArithmeticError::getLine' => ['int'], 'ArithmeticError::getMessage' => ['string'], 'ArithmeticError::getPrevious' => ['?Throwable'], - 'ArithmeticError::getTrace' => ['list\',args?:array}>'], + 'ArithmeticError::getTrace' => ['list\',args?:array}>'], 'ArithmeticError::getTraceAsString' => ['string'], 'ArrayAccess::offsetExists' => ['bool', 'offset'=>'mixed'], 'ArrayAccess::offsetGet' => ['mixed', 'offset'=>'mixed'], @@ -267,7 +267,7 @@ 'BadFunctionCallException::getLine' => ['int'], 'BadFunctionCallException::getMessage' => ['string'], 'BadFunctionCallException::getPrevious' => ['?Throwable|?BadFunctionCallException'], - 'BadFunctionCallException::getTrace' => ['list\',args?:array}>'], + 'BadFunctionCallException::getTrace' => ['list\',args?:array}>'], 'BadFunctionCallException::getTraceAsString' => ['string'], 'BadMethodCallException::__clone' => ['void'], 'BadMethodCallException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?BadMethodCallException'], @@ -277,7 +277,7 @@ 'BadMethodCallException::getLine' => ['int'], 'BadMethodCallException::getMessage' => ['string'], 'BadMethodCallException::getPrevious' => ['?Throwable|?BadMethodCallException'], - 'BadMethodCallException::getTrace' => ['list\',args?:array}>'], + 'BadMethodCallException::getTrace' => ['list\',args?:array}>'], 'BadMethodCallException::getTraceAsString' => ['string'], 'COM::__call' => ['', 'name'=>'', 'args'=>''], 'COM::__construct' => ['void', 'module_name'=>'string', 'server_name='=>'mixed', 'codepage='=>'int', 'typelib='=>'string'], @@ -536,7 +536,7 @@ 'ClosedGeneratorException::getLine' => ['int'], 'ClosedGeneratorException::getMessage' => ['string'], 'ClosedGeneratorException::getPrevious' => ['Throwable|ClosedGeneratorException|null'], - 'ClosedGeneratorException::getTrace' => ['list\',args?:array}>'], + 'ClosedGeneratorException::getTrace' => ['list\',args?:array}>'], 'ClosedGeneratorException::getTraceAsString' => ['string'], 'Closure::__construct' => ['void'], 'Closure::__invoke' => ['', '...args='=>''], @@ -1044,7 +1044,7 @@ 'DateTime::createFromFormat' => ['static|false', 'format'=>'string', 'time'=>'string', 'timezone='=>'?DateTimeZone'], 'DateTime::diff' => ['DateInterval|false', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTime::format' => ['string|false', 'format'=>'string'], - 'DateTime::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], + 'DateTime::getLastErrors' => ['strict-array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], 'DateTime::getOffset' => ['int'], 'DateTime::getTimestamp' => ['int|false'], 'DateTime::getTimezone' => ['DateTimeZone|false'], @@ -1057,7 +1057,7 @@ 'DateTime::sub' => ['static', 'interval'=>'DateInterval'], 'DateTimeImmutable::__set_state' => ['static', 'array'=>'array'], 'DateTimeImmutable::__wakeup' => ['void'], - 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], + 'DateTimeImmutable::getLastErrors' => ['strict-array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], 'DateTimeInterface::getOffset' => ['int'], @@ -1069,8 +1069,8 @@ 'DateTimeZone::getLocation' => ['array|false'], 'DateTimeZone::getName' => ['string'], 'DateTimeZone::getOffset' => ['int|false', 'datetime'=>'DateTimeInterface'], - 'DateTimeZone::getTransitions' => ['list|false', 'timestamp_begin='=>'int', 'timestamp_end='=>'int'], - 'DateTimeZone::listAbbreviations' => ['array>|false'], + 'DateTimeZone::getTransitions' => ['list|false', 'timestamp_begin='=>'int', 'timestamp_end='=>'int'], + 'DateTimeZone::listAbbreviations' => ['array>|false'], 'DateTimeZone::listIdentifiers' => ['list|false', 'timezoneGroup='=>'int', 'countryCode='=>'string'], 'Directory::close' => ['void', 'dir_handle='=>'resource'], 'Directory::read' => ['string|false', 'dir_handle='=>'resource'], @@ -1137,7 +1137,7 @@ 'DomainException::getLine' => ['int'], 'DomainException::getMessage' => ['string'], 'DomainException::getPrevious' => ['Throwable|DomainException|null'], - 'DomainException::getTrace' => ['list\',args?:array}>'], + 'DomainException::getTrace' => ['list\',args?:array}>'], 'DomainException::getTraceAsString' => ['string'], 'Ds\Collection::clear' => ['void'], 'Ds\Collection::copy' => ['Ds\Collection'], @@ -1362,7 +1362,7 @@ 'Error::getLine' => ['int'], 'Error::getMessage' => ['string'], 'Error::getPrevious' => ['Throwable|Error|null'], - 'Error::getTrace' => ['list\',args?:array}>'], + 'Error::getTrace' => ['list\',args?:array}>'], 'Error::getTraceAsString' => ['string'], 'ErrorException::__clone' => ['void'], 'ErrorException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'severity='=>'int', 'filename='=>'string', 'line='=>'int', 'previous='=>'?Throwable|?ErrorException'], @@ -1373,7 +1373,7 @@ 'ErrorException::getMessage' => ['string'], 'ErrorException::getPrevious' => ['Throwable|ErrorException|null'], 'ErrorException::getSeverity' => ['int'], - 'ErrorException::getTrace' => ['list\',args?:array}>'], + 'ErrorException::getTrace' => ['list\',args?:array}>'], 'ErrorException::getTraceAsString' => ['string'], 'Ev::backend' => ['int'], 'Ev::depth' => ['int'], @@ -1707,7 +1707,7 @@ 'Exception::getLine' => ['int'], 'Exception::getMessage' => ['string'], 'Exception::getPrevious' => ['?Throwable|?Exception'], - 'Exception::getTrace' => ['list\',args?:array}>'], + 'Exception::getTrace' => ['list\',args?:array}>'], 'Exception::getTraceAsString' => ['string'], 'FANNConnection::__construct' => ['void', 'from_neuron'=>'int', 'to_neuron'=>'int', 'weight'=>'float'], 'FANNConnection::getFromNeuron' => ['int'], @@ -1766,7 +1766,7 @@ 'GEOSGeometry::boundary' => ['GEOSGeometry'], 'GEOSGeometry::buffer' => ['GEOSGeometry', 'dist'=>'float', 'styleArray='=>'array'], 'GEOSGeometry::centroid' => ['GEOSGeometry'], - 'GEOSGeometry::checkValidity' => ['array{valid: bool, reason?: string, location?: GEOSGeometry}'], + 'GEOSGeometry::checkValidity' => ['strict-array{valid: bool, reason?: string, location?: GEOSGeometry}'], 'GEOSGeometry::contains' => ['bool', 'geom'=>'GEOSGeometry'], 'GEOSGeometry::convexHull' => ['GEOSGeometry'], 'GEOSGeometry::coordinateDimension' => ['int'], @@ -1824,7 +1824,7 @@ 'GEOSGeometry::voronoiDiagram' => ['GEOSGeometry', 'tolerance'=>'float', 'onlyEdges'=>'bool', 'extent'=>'GEOSGeometry|null'], 'GEOSGeometry::within' => ['bool', 'geom'=>'GEOSGeometry'], 'GEOSLineMerge' => ['array', 'geom'=>'GEOSGeometry'], - 'GEOSPolygonize' => ['array{rings: GEOSGeometry[], cut_edges?: GEOSGeometry[], dangles: GEOSGeometry[], invalid_rings: GEOSGeometry[]}', 'geom'=>'GEOSGeometry'], + 'GEOSPolygonize' => ['strict-array{rings: GEOSGeometry[], cut_edges?: GEOSGeometry[], dangles: GEOSGeometry[], invalid_rings: GEOSGeometry[]}', 'geom'=>'GEOSGeometry'], 'GEOSRelateMatch' => ['bool', 'matrix'=>'string', 'pattern'=>'string'], 'GEOSSharedPaths' => ['GEOSGeometry', 'geom1'=>'GEOSGeometry', 'geom2'=>'GEOSGeometry'], 'GEOSVersion' => ['string'], @@ -2661,9 +2661,9 @@ 'Imagick::colorizeImage' => ['bool', 'colorize'=>'mixed', 'opacity'=>'mixed'], 'Imagick::combineImages' => ['Imagick', 'channeltype'=>'int'], 'Imagick::commentImage' => ['bool', 'comment'=>'string'], - 'Imagick::compareImageChannels' => ['array{Imagick, float}', 'image'=>'Imagick', 'channeltype'=>'int', 'metrictype'=>'int'], + 'Imagick::compareImageChannels' => ['strict-array{Imagick, float}', 'image'=>'Imagick', 'channeltype'=>'int', 'metrictype'=>'int'], 'Imagick::compareImageLayers' => ['Imagick', 'method'=>'int'], - 'Imagick::compareImages' => ['array{Imagick, float}', 'compare'=>'Imagick', 'metric'=>'int'], + 'Imagick::compareImages' => ['strict-array{Imagick, float}', 'compare'=>'Imagick', 'metric'=>'int'], 'Imagick::compositeImage' => ['bool', 'composite_object'=>'Imagick', 'composite'=>'int', 'x'=>'int', 'y'=>'int', 'channel='=>'int'], 'Imagick::compositeImageGravity' => ['bool', 'Imagick'=>'Imagick', 'COMPOSITE_CONSTANT'=>'int', 'GRAVITY_CONSTANT'=>'int'], 'Imagick::contrastImage' => ['bool', 'sharpen'=>'bool'], @@ -2723,16 +2723,16 @@ 'Imagick::getImageAttribute' => ['string', 'key'=>'string'], 'Imagick::getImageBackgroundColor' => ['ImagickPixel'], 'Imagick::getImageBlob' => ['string'], - 'Imagick::getImageBluePrimary' => ['array{x:float, y:float}'], + 'Imagick::getImageBluePrimary' => ['strict-array{x:float, y:float}'], 'Imagick::getImageBorderColor' => ['ImagickPixel'], 'Imagick::getImageChannelDepth' => ['int', 'channel'=>'int'], 'Imagick::getImageChannelDistortion' => ['float', 'reference'=>'Imagick', 'channel'=>'int', 'metric'=>'int'], 'Imagick::getImageChannelDistortions' => ['float', 'reference'=>'Imagick', 'metric'=>'int', 'channel='=>'int'], - 'Imagick::getImageChannelExtrema' => ['array{minima:int, maxima:int}', 'channel'=>'int'], - 'Imagick::getImageChannelKurtosis' => ['array{kurtosis:float, skewness:float}', 'channel='=>'int'], - 'Imagick::getImageChannelMean' => ['array{mean:float, standardDeviation:float}', 'channel'=>'int'], - 'Imagick::getImageChannelRange' => ['array{minima:float, maxima:float}', 'channel'=>'int'], - 'Imagick::getImageChannelStatistics' => ['array'], + 'Imagick::getImageChannelExtrema' => ['strict-array{minima:int, maxima:int}', 'channel'=>'int'], + 'Imagick::getImageChannelKurtosis' => ['strict-array{kurtosis:float, skewness:float}', 'channel='=>'int'], + 'Imagick::getImageChannelMean' => ['strict-array{mean:float, standardDeviation:float}', 'channel'=>'int'], + 'Imagick::getImageChannelRange' => ['strict-array{minima:float, maxima:float}', 'channel'=>'int'], + 'Imagick::getImageChannelStatistics' => ['array'], 'Imagick::getImageClipMask' => ['Imagick'], 'Imagick::getImageColormapColor' => ['ImagickPixel', 'index'=>'int'], 'Imagick::getImageColors' => ['int'], @@ -2744,13 +2744,13 @@ 'Imagick::getImageDepth' => ['int'], 'Imagick::getImageDispose' => ['int'], 'Imagick::getImageDistortion' => ['float', 'reference'=>'magickwand', 'metric'=>'int'], - 'Imagick::getImageExtrema' => ['array{min:int, max:int}'], + 'Imagick::getImageExtrema' => ['strict-array{min:int, max:int}'], 'Imagick::getImageFilename' => ['string'], 'Imagick::getImageFormat' => ['string'], 'Imagick::getImageGamma' => ['float'], - 'Imagick::getImageGeometry' => ['array{width:int, height:int}'], + 'Imagick::getImageGeometry' => ['strict-array{width:int, height:int}'], 'Imagick::getImageGravity' => ['int'], - 'Imagick::getImageGreenPrimary' => ['array{x:float, y:float}'], + 'Imagick::getImageGreenPrimary' => ['strict-array{x:float, y:float}'], 'Imagick::getImageHeight' => ['int'], 'Imagick::getImageHistogram' => ['list'], 'Imagick::getImageIndex' => ['int'], @@ -2763,16 +2763,16 @@ 'Imagick::getImageMatteColor' => ['ImagickPixel'], 'Imagick::getImageMimeType' => ['string'], 'Imagick::getImageOrientation' => ['int'], - 'Imagick::getImagePage' => ['array{width:int, height:int, x:int, y:int}'], + 'Imagick::getImagePage' => ['strict-array{width:int, height:int, x:int, y:int}'], 'Imagick::getImagePixelColor' => ['ImagickPixel', 'x'=>'int', 'y'=>'int'], 'Imagick::getImageProfile' => ['string', 'name'=>'string'], 'Imagick::getImageProfiles' => ['array', 'pattern='=>'string', 'only_names='=>'bool'], 'Imagick::getImageProperties' => ['array', 'pattern='=>'string', 'only_names='=>'bool'], 'Imagick::getImageProperty' => ['string|false', 'name'=>'string'], - 'Imagick::getImageRedPrimary' => ['array{x:float, y:float}'], + 'Imagick::getImageRedPrimary' => ['strict-array{x:float, y:float}'], 'Imagick::getImageRegion' => ['Imagick', 'width'=>'int', 'height'=>'int', 'x'=>'int', 'y'=>'int'], 'Imagick::getImageRenderingIntent' => ['int'], - 'Imagick::getImageResolution' => ['array{x:float, y:float}'], + 'Imagick::getImageResolution' => ['strict-array{x:float, y:float}'], 'Imagick::getImageScene' => ['int'], 'Imagick::getImageSignature' => ['string'], 'Imagick::getImageSize' => ['int'], @@ -2781,7 +2781,7 @@ 'Imagick::getImageType' => ['int'], 'Imagick::getImageUnits' => ['int'], 'Imagick::getImageVirtualPixelMethod' => ['int'], - 'Imagick::getImageWhitePoint' => ['array{x:float, y:float}'], + 'Imagick::getImageWhitePoint' => ['strict-array{x:float, y:float}'], 'Imagick::getImageWidth' => ['int'], 'Imagick::getImagesBlob' => ['string'], 'Imagick::getInterlaceScheme' => ['int'], @@ -2789,21 +2789,21 @@ 'Imagick::getNumberImages' => ['int'], 'Imagick::getOption' => ['string', 'key'=>'string'], 'Imagick::getPackageName' => ['string'], - 'Imagick::getPage' => ['array{width:int, height:int, x:int, y:int}'], + 'Imagick::getPage' => ['strict-array{width:int, height:int, x:int, y:int}'], 'Imagick::getPixelIterator' => ['ImagickPixelIterator'], 'Imagick::getPixelRegionIterator' => ['ImagickPixelIterator', 'x'=>'int', 'y'=>'int', 'columns'=>'int', 'rows'=>'int'], 'Imagick::getPointSize' => ['float'], 'Imagick::getQuantum' => ['int'], - 'Imagick::getQuantumDepth' => ['array{quantumDepthLong:int, quantumDepthString:string}'], - 'Imagick::getQuantumRange' => ['array{quantumRangeLong:int, quantumRangeString:string}'], + 'Imagick::getQuantumDepth' => ['strict-array{quantumDepthLong:int, quantumDepthString:string}'], + 'Imagick::getQuantumRange' => ['strict-array{quantumRangeLong:int, quantumRangeString:string}'], 'Imagick::getRegistry' => ['string|false', 'key'=>'string'], 'Imagick::getReleaseDate' => ['string'], 'Imagick::getResource' => ['int', 'type'=>'int'], 'Imagick::getResourceLimit' => ['int', 'type'=>'int'], 'Imagick::getSamplingFactors' => ['array'], - 'Imagick::getSize' => ['array{columns:int, rows: int}'], + 'Imagick::getSize' => ['strict-array{columns:int, rows: int}'], 'Imagick::getSizeOffset' => ['int'], - 'Imagick::getVersion' => ['array{versionNumber: int, versionString:string}'], + 'Imagick::getVersion' => ['strict-array{versionNumber: int, versionString:string}'], 'Imagick::haldClutImage' => ['bool', 'clut'=>'Imagick', 'channel='=>'int'], 'Imagick::hasNextImage' => ['bool'], 'Imagick::hasPreviousImage' => ['bool'], @@ -3144,13 +3144,13 @@ 'ImagickPixel::clear' => ['bool'], 'ImagickPixel::clone' => ['void'], 'ImagickPixel::destroy' => ['bool'], - 'ImagickPixel::getColor' => ['array{r: int|float, g: int|float, b: int|float, a: int|float}', 'normalized='=>'0|1|2'], + 'ImagickPixel::getColor' => ['strict-array{r: int|float, g: int|float, b: int|float, a: int|float}', 'normalized='=>'0|1|2'], 'ImagickPixel::getColorAsString' => ['string'], 'ImagickPixel::getColorCount' => ['int'], 'ImagickPixel::getColorQuantum' => ['mixed'], 'ImagickPixel::getColorValue' => ['float', 'color'=>'int'], 'ImagickPixel::getColorValueQuantum' => ['mixed'], - 'ImagickPixel::getHSL' => ['array{hue: float, saturation: float, luminosity: float}'], + 'ImagickPixel::getHSL' => ['strict-array{hue: float, saturation: float, luminosity: float}'], 'ImagickPixel::getIndex' => ['int'], 'ImagickPixel::isPixelSimilar' => ['bool', 'color'=>'ImagickPixel', 'fuzz'=>'float'], 'ImagickPixel::isPixelSimilarQuantum' => ['bool', 'color'=>'string', 'fuzz='=>'string'], @@ -3371,7 +3371,7 @@ 'IntlException::getLine' => ['int'], 'IntlException::getMessage' => ['string'], 'IntlException::getPrevious' => ['?Throwable'], - 'IntlException::getTrace' => ['list\',args?:array}>'], + 'IntlException::getTrace' => ['list\',args?:array}>'], 'IntlException::getTraceAsString' => ['string'], 'IntlGregorianCalendar::__construct' => ['void'], 'IntlGregorianCalendar::add' => ['bool', 'field'=>'int', 'amount'=>'int'], @@ -3487,7 +3487,7 @@ 'InvalidArgumentException::getLine' => ['int'], 'InvalidArgumentException::getMessage' => ['string'], 'InvalidArgumentException::getPrevious' => ['Throwable|InvalidArgumentException|null'], - 'InvalidArgumentException::getTrace' => ['list\',args?:array}>'], + 'InvalidArgumentException::getTrace' => ['list\',args?:array}>'], 'InvalidArgumentException::getTraceAsString' => ['string'], 'Iterator::current' => ['mixed'], 'Iterator::key' => ['mixed'], @@ -3583,7 +3583,7 @@ 'LengthException::getLine' => ['int'], 'LengthException::getMessage' => ['string'], 'LengthException::getPrevious' => ['Throwable|LengthException|null'], - 'LengthException::getTrace' => ['list\',args?:array}>'], + 'LengthException::getTrace' => ['list\',args?:array}>'], 'LengthException::getTraceAsString' => ['string'], 'LevelDB::__construct' => ['void', 'name'=>'string', 'options='=>'array', 'read_options='=>'array', 'write_options='=>'array'], 'LevelDB::close' => [''], @@ -3652,7 +3652,7 @@ 'LogicException::getLine' => ['int'], 'LogicException::getMessage' => ['string'], 'LogicException::getPrevious' => ['Throwable|LogicException|null'], - 'LogicException::getTrace' => ['list\',args?:array}>'], + 'LogicException::getTrace' => ['list\',args?:array}>'], 'LogicException::getTraceAsString' => ['string'], 'Lua::__call' => ['mixed', 'lua_func'=>'callable', 'args='=>'array', 'use_self='=>'int'], 'Lua::__construct' => ['void', 'lua_script_file'=>'string'], @@ -3919,7 +3919,7 @@ 'MongoCursorException::getLine' => ['int'], 'MongoCursorException::getMessage' => ['string'], 'MongoCursorException::getPrevious' => ['Exception|Throwable'], - 'MongoCursorException::getTrace' => ['list\',args?:array}>'], + 'MongoCursorException::getTrace' => ['list\',args?:array}>'], 'MongoCursorException::getTraceAsString' => ['string'], 'MongoCursorInterface::__construct' => ['void'], 'MongoCursorInterface::batchSize' => ['MongoCursorInterface', 'batchSize'=>'int'], @@ -4287,7 +4287,7 @@ 'MongoException::getLine' => ['int'], 'MongoException::getMessage' => ['string'], 'MongoException::getPrevious' => ['Exception|Throwable'], - 'MongoException::getTrace' => ['list\',args?:array}>'], + 'MongoException::getTrace' => ['list\',args?:array}>'], 'MongoException::getTraceAsString' => ['string'], 'MongoGridFS::__construct' => ['void', 'db'=>'MongoDB', 'prefix='=>'string', 'chunks='=>'mixed'], 'MongoGridFS::__get' => ['MongoCollection', 'name'=>'string'], @@ -4398,7 +4398,7 @@ 'MongoResultException::getLine' => ['int'], 'MongoResultException::getMessage' => ['string'], 'MongoResultException::getPrevious' => ['Exception|Throwable'], - 'MongoResultException::getTrace' => ['list\',args?:array}>'], + 'MongoResultException::getTrace' => ['list\',args?:array}>'], 'MongoResultException::getTraceAsString' => ['string'], 'MongoTimestamp::__construct' => ['void', 'second='=>'int', 'inc='=>'int'], 'MongoTimestamp::__toString' => ['string'], @@ -4418,7 +4418,7 @@ 'MongoWriteConcernException::getLine' => ['int'], 'MongoWriteConcernException::getMessage' => ['string'], 'MongoWriteConcernException::getPrevious' => ['Exception|Throwable'], - 'MongoWriteConcernException::getTrace' => ['list\',args?:array}>'], + 'MongoWriteConcernException::getTrace' => ['list\',args?:array}>'], 'MongoWriteConcernException::getTraceAsString' => ['string'], 'MultipleIterator::__construct' => ['void', 'flags='=>'int'], 'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'infos='=>'string'], @@ -4599,7 +4599,7 @@ 'OutOfBoundsException::getLine' => ['int'], 'OutOfBoundsException::getMessage' => ['string'], 'OutOfBoundsException::getPrevious' => ['Throwable|OutOfBoundsException|null'], - 'OutOfBoundsException::getTrace' => ['list\',args?:array}>'], + 'OutOfBoundsException::getTrace' => ['list\',args?:array}>'], 'OutOfBoundsException::getTraceAsString' => ['string'], 'OutOfRangeException::__clone' => ['void'], 'OutOfRangeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?OutOfRangeException'], @@ -4609,7 +4609,7 @@ 'OutOfRangeException::getLine' => ['int'], 'OutOfRangeException::getMessage' => ['string'], 'OutOfRangeException::getPrevious' => ['Throwable|OutOfRangeException|null'], - 'OutOfRangeException::getTrace' => ['list\',args?:array}>'], + 'OutOfRangeException::getTrace' => ['list\',args?:array}>'], 'OutOfRangeException::getTraceAsString' => ['string'], 'OuterIterator::current' => ['mixed'], 'OuterIterator::getInnerIterator' => ['Iterator'], @@ -4625,7 +4625,7 @@ 'OverflowException::getLine' => ['int'], 'OverflowException::getMessage' => ['string'], 'OverflowException::getPrevious' => ['Throwable|OverflowException|null'], - 'OverflowException::getTrace' => ['list\',args?:array}>'], + 'OverflowException::getTrace' => ['list\',args?:array}>'], 'OverflowException::getTraceAsString' => ['string'], 'OwsrequestObj::__construct' => ['void'], 'OwsrequestObj::addParameter' => ['int', 'name'=>'string', 'value'=>'string'], @@ -4948,7 +4948,7 @@ 'PDO::commit' => ['bool'], 'PDO::cubrid_schema' => ['array', 'schema_type'=>'int', 'table_name='=>'string', 'col_name='=>'string'], 'PDO::errorCode' => ['?string'], - 'PDO::errorInfo' => ['array{0: ?string, 1: ?int, 2: ?string, 3?: mixed, 4?: mixed}'], + 'PDO::errorInfo' => ['strict-array{0: ?string, 1: ?int, 2: ?string, 3?: mixed, 4?: mixed}'], 'PDO::exec' => ['int|false', 'query'=>'string'], 'PDO::getAttribute' => ['', 'attribute'=>'int'], 'PDO::getAvailableDrivers' => ['array'], @@ -4958,7 +4958,7 @@ 'PDO::pgsqlCopyFromFile' => ['bool', 'table_name'=>'string', 'filename'=>'string', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'], 'PDO::pgsqlCopyToArray' => ['array', 'table_name'=>'string', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'], 'PDO::pgsqlCopyToFile' => ['bool', 'table_name'=>'string', 'filename'=>'string', 'delimiter'=>'string', 'null_as'=>'string', 'fields'=>'string'], - 'PDO::pgsqlGetNotify' => ['array{message:string,pid:int,payload?:string}|false', 'result_type='=>'PDO::FETCH_*', 'ms_timeout='=>'int'], + 'PDO::pgsqlGetNotify' => ['strict-array{message:string,pid:int,payload?:string}|false', 'result_type='=>'PDO::FETCH_*', 'ms_timeout='=>'int'], 'PDO::pgsqlGetPid' => ['int'], 'PDO::pgsqlLOBCreate' => ['string'], 'PDO::pgsqlLOBOpen' => ['resource', 'oid'=>'string', 'mode='=>'string'], @@ -4979,7 +4979,7 @@ 'PDOException::getLine' => ['int'], 'PDOException::getMessage' => ['string'], 'PDOException::getPrevious' => ['?Throwable'], - 'PDOException::getTrace' => ['list\',args?:array}>'], + 'PDOException::getTrace' => ['list\',args?:array}>'], 'PDOException::getTraceAsString' => ['string'], 'PDOStatement::__sleep' => ['list'], 'PDOStatement::__wakeup' => ['void'], @@ -4990,7 +4990,7 @@ 'PDOStatement::columnCount' => ['int'], 'PDOStatement::debugDumpParams' => ['void'], 'PDOStatement::errorCode' => ['string'], - 'PDOStatement::errorInfo' => ['array{0: ?string, 1: ?int, 2: ?string, 3?: mixed, 4?: mixed}'], + 'PDOStatement::errorInfo' => ['strict-array{0: ?string, 1: ?int, 2: ?string, 3?: mixed, 4?: mixed}'], 'PDOStatement::execute' => ['bool', 'bound_input_params='=>'?array'], 'PDOStatement::fetch' => ['mixed', 'how='=>'int', 'orientation='=>'int', 'offset='=>'int'], 'PDOStatement::fetchAll' => ['array|false', 'how='=>'int', 'fetch_argument='=>'int|string|callable', 'ctor_args='=>'?array'], @@ -5072,7 +5072,7 @@ 'ParseError::getLine' => ['int'], 'ParseError::getMessage' => ['string'], 'ParseError::getPrevious' => ['Throwable|ParseError|null'], - 'ParseError::getTrace' => ['list\',args?:array}>'], + 'ParseError::getTrace' => ['list\',args?:array}>'], 'ParseError::getTraceAsString' => ['string'], 'Phar::__construct' => ['void', 'fname'=>'string', 'flags='=>'int', 'alias='=>'string'], 'Phar::addEmptyDir' => ['void', 'dirname'=>'string'], @@ -5101,7 +5101,7 @@ 'Phar::getMetadata' => ['mixed'], 'Phar::getModified' => ['bool'], 'Phar::getPath' => ['string'], - 'Phar::getSignature' => ['array{hash:string, hash_type:string}'], + 'Phar::getSignature' => ['strict-array{hash:string, hash_type:string}'], 'Phar::getStub' => ['string'], 'Phar::getSupportedCompression' => ['array'], 'Phar::getSupportedSignatures' => ['array'], @@ -5246,7 +5246,7 @@ 'RangeException::getLine' => ['int'], 'RangeException::getMessage' => ['string'], 'RangeException::getPrevious' => ['Throwable|RangeException|null'], - 'RangeException::getTrace' => ['list\',args?:array}>'], + 'RangeException::getTrace' => ['list\',args?:array}>'], 'RangeException::getTraceAsString' => ['string'], 'RarArchive::__toString' => ['string'], 'RarArchive::close' => ['bool'], @@ -5276,7 +5276,7 @@ 'RarException::getLine' => ['int'], 'RarException::getMessage' => ['string'], 'RarException::getPrevious' => ['Exception|Throwable'], - 'RarException::getTrace' => ['list\',args?:array}>'], + 'RarException::getTrace' => ['list\',args?:array}>'], 'RarException::getTraceAsString' => ['string'], 'RarException::isUsingExceptions' => ['bool'], 'RarException::setUsingExceptions' => ['RarEntry', 'using_exceptions'=>'bool'], @@ -5590,7 +5590,7 @@ 'Redis::geoAdd' => ['int', 'key'=>'string', 'longitude'=>'float', 'latitude'=>'float', 'member'=>'string', '...other_triples='=>'string|int|float'], 'Redis::geoDist' => ['float', 'key'=>'string', 'member1'=>'string', 'member2'=>'string', 'unit='=>'string'], 'Redis::geoHash' => ['array', 'key'=>'string', 'member'=>'string', '...other_members='=>'string'], - 'Redis::geoPos' => ['array', 'key'=>'string', 'member'=>'string', '...members='=>'string'], + 'Redis::geoPos' => ['array', 'key'=>'string', 'member'=>'string', '...members='=>'string'], 'Redis::geoRadius' => ['array|int', 'key'=>'string', 'longitude'=>'float', 'latitude'=>'float', 'radius'=>'float', 'unit'=>'float', 'options='=>'array'], 'Redis::geoRadiusByMember' => ['array|int', 'key'=>'string', 'member'=>'string', 'radius'=>'float', 'units'=>'string', 'options='=>'array'], 'Redis::get' => ['string|false', 'key'=>'string'], @@ -5855,7 +5855,7 @@ 'RedisCluster::geoRadius' => ['', 'key'=>'string', 'longitude'=>'float', 'latitude'=>'float', 'radius'=>'float', 'radiusUnit'=>'string', 'options='=>'array'], 'RedisCluster::geoRadiusByMember' => ['string[]', 'key'=>'string', 'member'=>'string', 'radius'=>'float', 'radiusUnit'=>'string', 'options='=>'array'], 'RedisCluster::geohash' => ['array', 'key'=>'string', 'member'=>'string', '...other_members='=>'string'], - 'RedisCluster::geopos' => ['array', 'key'=>'string', 'member'=>'string', '...other_members='=>'string'], + 'RedisCluster::geopos' => ['array', 'key'=>'string', 'member'=>'string', '...other_members='=>'string'], 'RedisCluster::get' => ['string|false', 'key'=>'string'], 'RedisCluster::getBit' => ['int', 'key'=>'string', 'offset'=>'int'], 'RedisCluster::getLastError' => ['?string'], @@ -6333,7 +6333,7 @@ 'RuntimeException::getLine' => ['int'], 'RuntimeException::getMessage' => ['string'], 'RuntimeException::getPrevious' => ['Throwable|RuntimeException|null'], - 'RuntimeException::getTrace' => ['list\',args?:array}>'], + 'RuntimeException::getTrace' => ['list\',args?:array}>'], 'RuntimeException::getTraceAsString' => ['string'], 'SAMConnection::commit' => ['bool'], 'SAMConnection::connect' => ['bool', 'protocol'=>'string', 'properties='=>'array'], @@ -6493,7 +6493,7 @@ 'SQLiteException::getLine' => ['int'], 'SQLiteException::getMessage' => ['string'], 'SQLiteException::getPrevious' => ['RuntimeException|Throwable|null'], - 'SQLiteException::getTrace' => ['list\',args?:array}>'], + 'SQLiteException::getTrace' => ['list\',args?:array}>'], 'SQLiteException::getTraceAsString' => ['string'], 'SQLiteResult::__construct' => ['void'], 'SQLiteResult::column' => ['mixed', 'index_or_name'=>'', 'decode_binary='=>'bool'], @@ -6927,7 +6927,7 @@ 'SoapFault::getLine' => ['int'], 'SoapFault::getMessage' => ['string'], 'SoapFault::getPrevious' => ['?Exception|?Throwable'], - 'SoapFault::getTrace' => ['list\',args?:array}>'], + 'SoapFault::getTrace' => ['list\',args?:array}>'], 'SoapFault::getTraceAsString' => ['string'], 'SoapHeader::SoapHeader' => ['object', 'namespace'=>'string', 'name'=>'string', 'data='=>'mixed', 'mustunderstand='=>'bool', 'actor='=>'string'], 'SoapHeader::__construct' => ['void', 'namespace'=>'string', 'name'=>'string', 'data='=>'mixed', 'mustunderstand='=>'bool', 'actor='=>'string'], @@ -7037,7 +7037,7 @@ 'SolrClientException::getLine' => ['int'], 'SolrClientException::getMessage' => ['string'], 'SolrClientException::getPrevious' => ['?Exception|?Throwable'], - 'SolrClientException::getTrace' => ['list\',args?:array}>'], + 'SolrClientException::getTrace' => ['list\',args?:array}>'], 'SolrClientException::getTraceAsString' => ['string'], 'SolrCollapseFunction::__construct' => ['void', 'field'=>'string'], 'SolrCollapseFunction::__toString' => ['string'], @@ -7329,7 +7329,7 @@ 'SolrException::getLine' => ['int'], 'SolrException::getMessage' => ['string'], 'SolrException::getPrevious' => ['Exception|Throwable'], - 'SolrException::getTrace' => ['list\',args?:array}>'], + 'SolrException::getTrace' => ['list\',args?:array}>'], 'SolrException::getTraceAsString' => ['string'], 'SolrGenericResponse::__construct' => ['void'], 'SolrGenericResponse::__destruct' => ['void'], @@ -7354,7 +7354,7 @@ 'SolrIllegalArgumentException::getLine' => ['int'], 'SolrIllegalArgumentException::getMessage' => ['string'], 'SolrIllegalArgumentException::getPrevious' => ['Exception|Throwable'], - 'SolrIllegalArgumentException::getTrace' => ['list\',args?:array}>'], + 'SolrIllegalArgumentException::getTrace' => ['list\',args?:array}>'], 'SolrIllegalArgumentException::getTraceAsString' => ['string'], 'SolrIllegalOperationException::__clone' => ['void'], 'SolrIllegalOperationException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Exception|?Throwable'], @@ -7366,7 +7366,7 @@ 'SolrIllegalOperationException::getLine' => ['int'], 'SolrIllegalOperationException::getMessage' => ['string'], 'SolrIllegalOperationException::getPrevious' => ['Exception|Throwable'], - 'SolrIllegalOperationException::getTrace' => ['list\',args?:array}>'], + 'SolrIllegalOperationException::getTrace' => ['list\',args?:array}>'], 'SolrIllegalOperationException::getTraceAsString' => ['string'], 'SolrInputDocument::__clone' => ['void'], 'SolrInputDocument::__construct' => ['void'], @@ -7674,7 +7674,7 @@ 'SolrServerException::getLine' => ['int'], 'SolrServerException::getMessage' => ['string'], 'SolrServerException::getPrevious' => ['Exception|Throwable'], - 'SolrServerException::getTrace' => ['list\',args?:array}>'], + 'SolrServerException::getTrace' => ['list\',args?:array}>'], 'SolrServerException::getTraceAsString' => ['string'], 'SolrUpdateResponse::__construct' => ['void'], 'SolrUpdateResponse::__destruct' => ['void'], @@ -7790,7 +7790,7 @@ 'SplFileObject::eof' => ['bool'], 'SplFileObject::fflush' => ['bool'], 'SplFileObject::fgetc' => ['string|false'], - 'SplFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'SplFileObject::fgetcsv' => ['list|strict-array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplFileObject::fgets' => ['string|false'], 'SplFileObject::fgetss' => ['string|false', 'allowable_tags='=>'string'], 'SplFileObject::flock' => ['bool', 'operation'=>'int', '&w_wouldblock='=>'int'], @@ -7799,7 +7799,7 @@ 'SplFileObject::fread' => ['string|false', 'length'=>'int'], 'SplFileObject::fscanf' => ['array|int', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'SplFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], - 'SplFileObject::fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], + 'SplFileObject::fstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], 'SplFileObject::ftell' => ['int|false'], 'SplFileObject::ftruncate' => ['bool', 'size'=>'int'], 'SplFileObject::fwrite' => ['int', 'string'=>'string', 'length='=>'int'], @@ -7982,7 +7982,7 @@ 'SplTempFileObject::eof' => ['bool'], 'SplTempFileObject::fflush' => ['bool'], 'SplTempFileObject::fgetc' => ['false|string'], - 'SplTempFileObject::fgetcsv' => ['list|array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'SplTempFileObject::fgetcsv' => ['list|strict-array{0: null}|false|null', 'seperator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'SplTempFileObject::fgets' => ['string'], 'SplTempFileObject::fgetss' => ['string', 'allowable_tags='=>'string'], 'SplTempFileObject::flock' => ['bool', 'operation'=>'int', '&wouldblock='=>'int'], @@ -7991,7 +7991,7 @@ 'SplTempFileObject::fread' => ['false|string', 'length'=>'int'], 'SplTempFileObject::fscanf' => ['bool', 'format'=>'string', '&...w_vars='=>'array|array|array'], 'SplTempFileObject::fseek' => ['int', 'pos'=>'int', 'whence='=>'int'], - 'SplTempFileObject::fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], + 'SplTempFileObject::fstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}'], 'SplTempFileObject::ftell' => ['int'], 'SplTempFileObject::ftruncate' => ['bool', 'size'=>'int'], 'SplTempFileObject::fwrite' => ['int', 'string'=>'string', 'length='=>'int'], @@ -8169,7 +8169,7 @@ 'Throwable::getLine' => ['int'], 'Throwable::getMessage' => ['string'], 'Throwable::getPrevious' => ['?Throwable'], - 'Throwable::getTrace' => ['list\',args?:array}>'], + 'Throwable::getTrace' => ['list\',args?:array}>'], 'Throwable::getTraceAsString' => ['string'], 'TokyoTyrant::__construct' => ['void', 'host='=>'string', 'port='=>'int', 'options='=>'array'], 'TokyoTyrant::add' => ['int|float', 'key'=>'string', 'increment'=>'float', 'type='=>'int'], @@ -8241,7 +8241,7 @@ 'TypeError::getLine' => ['int'], 'TypeError::getMessage' => ['string'], 'TypeError::getPrevious' => ['Throwable|TypeError|null'], - 'TypeError::getTrace' => ['list\',args?:array}>'], + 'TypeError::getTrace' => ['list\',args?:array}>'], 'TypeError::getTraceAsString' => ['string'], 'UConverter::__construct' => ['void', 'destination_encoding='=>'string', 'source_encoding='=>'string'], 'UConverter::convert' => ['string', 'string'=>'string', 'reverse='=>'bool'], @@ -8270,7 +8270,7 @@ 'UnderflowException::getLine' => ['int'], 'UnderflowException::getMessage' => ['string'], 'UnderflowException::getPrevious' => ['Throwable|UnderflowException|null'], - 'UnderflowException::getTrace' => ['list\',args?:array}>'], + 'UnderflowException::getTrace' => ['list\',args?:array}>'], 'UnderflowException::getTraceAsString' => ['string'], 'UnexpectedValueException::__clone' => ['void'], 'UnexpectedValueException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable|?UnexpectedValueException'], @@ -8280,7 +8280,7 @@ 'UnexpectedValueException::getLine' => ['int'], 'UnexpectedValueException::getMessage' => ['string'], 'UnexpectedValueException::getPrevious' => ['Throwable|UnexpectedValueException|null'], - 'UnexpectedValueException::getTrace' => ['list\',args?:array}>'], + 'UnexpectedValueException::getTrace' => ['list\',args?:array}>'], 'UnexpectedValueException::getTraceAsString' => ['string'], 'V8Js::__construct' => ['void', 'object_name='=>'string', 'variables='=>'array', 'extensions='=>'array', 'report_uncaught_exceptions='=>'bool', 'snapshot_blob='=>'string'], 'V8Js::clearPendingException' => [''], @@ -8315,7 +8315,7 @@ 'V8JsScriptException::getLine' => ['int'], 'V8JsScriptException::getMessage' => ['string'], 'V8JsScriptException::getPrevious' => ['Exception|Throwable'], - 'V8JsScriptException::getTrace' => ['list\',args?:array}>'], + 'V8JsScriptException::getTrace' => ['list\',args?:array}>'], 'V8JsScriptException::getTraceAsString' => ['string'], 'VARIANT::__construct' => ['void', 'value='=>'mixed', 'type='=>'int', 'codepage='=>'int'], 'VarnishAdmin::__construct' => ['void', 'args='=>'array'], @@ -9333,7 +9333,7 @@ 'Yar_Client_Exception::getLine' => ['int'], 'Yar_Client_Exception::getMessage' => ['string'], 'Yar_Client_Exception::getPrevious' => ['?Exception|?Throwable'], - 'Yar_Client_Exception::getTrace' => ['list\',args?:array}>'], + 'Yar_Client_Exception::getTrace' => ['list\',args?:array}>'], 'Yar_Client_Exception::getTraceAsString' => ['string'], 'Yar_Client_Exception::getType' => ['string'], 'Yar_Concurrent_Client::call' => ['int', 'uri'=>'string', 'method'=>'string', 'parameters'=>'array', 'callback='=>'callable'], @@ -9350,7 +9350,7 @@ 'Yar_Server_Exception::getLine' => ['int'], 'Yar_Server_Exception::getMessage' => ['string'], 'Yar_Server_Exception::getPrevious' => ['?Exception|?Throwable'], - 'Yar_Server_Exception::getTrace' => ['list\',args?:array}>'], + 'Yar_Server_Exception::getTrace' => ['list\',args?:array}>'], 'Yar_Server_Exception::getTraceAsString' => ['string'], 'Yar_Server_Exception::getType' => ['string'], 'ZMQ::__construct' => ['void'], @@ -9936,7 +9936,7 @@ 'cairo_version' => ['int'], 'cairo_version_string' => ['string'], 'cal_days_in_month' => ['int', 'calendar'=>'int', 'month'=>'int', 'year'=>'int'], - 'cal_from_jd' => ['false|array{date:string,month:int,day:int,year:int,dow:int,abbrevdayname:string,dayname:string,abbrevmonth:string,monthname:string}', 'julian_day'=>'int', 'calendar'=>'int'], + 'cal_from_jd' => ['false|strict-array{date:string,month:int,day:int,year:int,dow:int,abbrevdayname:string,dayname:string,abbrevmonth:string,monthname:string}', 'julian_day'=>'int', 'calendar'=>'int'], 'cal_info' => ['array', 'calendar='=>'int'], 'cal_to_jd' => ['int', 'calendar'=>'int', 'month'=>'int', 'day'=>'int', 'year'=>'int'], 'calcul_hmac' => ['string', 'clent'=>'string', 'siretcode'=>'string', 'price'=>'string', 'reference'=>'string', 'validity'=>'string', 'taxation'=>'string', 'devise'=>'string', 'language'=>'string'], @@ -10254,7 +10254,7 @@ 'date_default_timezone_set' => ['bool', 'timezoneId'=>'string'], 'date_diff' => ['DateInterval|false', 'baseObject'=>'DateTimeInterface', 'targetObject'=>'DateTimeInterface', 'absolute='=>'bool'], 'date_format' => ['string|false', 'object'=>'DateTimeInterface', 'format'=>'string'], - 'date_get_last_errors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], + 'date_get_last_errors' => ['strict-array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], 'date_interval_create_from_date_string' => ['DateInterval', 'datetime'=>'string'], 'date_interval_format' => ['string', 'object'=>'DateInterval', 'format'=>'string'], 'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int|mixed'], @@ -10433,7 +10433,7 @@ 'dcgettext' => ['string', 'domain'=>'string', 'message'=>'string', 'category'=>'int'], 'dcngettext' => ['string', 'domain'=>'string', 'singular'=>'string', 'plural'=>'string', 'count'=>'int', 'category'=>'int'], 'deaggregate' => ['', 'object'=>'object', 'class_name='=>'string'], - 'debug_backtrace' => ['list', 'options='=>'int', 'limit='=>'int'], + 'debug_backtrace' => ['list', 'options='=>'int', 'limit='=>'int'], 'debug_print_backtrace' => ['void', 'options='=>'int', 'limit='=>'int'], 'debug_zval_dump' => ['void', '...value'=>'mixed'], 'debugger_connect' => [''], @@ -10492,7 +10492,7 @@ 'domxml_xslt_version' => ['int'], 'dotnet_load' => ['int', 'assembly_name'=>'string', 'datatype_name='=>'string', 'codepage='=>'int'], 'doubleval' => ['float', 'value'=>'mixed'], - 'each' => ['array{0:int|string,key:int|string,1:mixed,value:mixed}', '&r_arr'=>'array'], + 'each' => ['strict-array{0:int|string,key:int|string,1:mixed,value:mixed}', '&r_arr'=>'array'], 'easter_date' => ['int', 'year='=>'int'], 'easter_days' => ['int', 'year='=>'int', 'mode='=>'int'], 'echo' => ['void', 'arg1'=>'string', '...args='=>'string'], @@ -10563,7 +10563,7 @@ 'enchant_broker_get_dict_path' => ['string', 'broker'=>'resource', 'type'=>'int'], 'enchant_broker_get_error' => ['string|false', 'broker'=>'resource'], 'enchant_broker_init' => ['resource|false'], - 'enchant_broker_list_dicts' => ['array|false', 'broker'=>'resource'], + 'enchant_broker_list_dicts' => ['array|false', 'broker'=>'resource'], 'enchant_broker_request_dict' => ['resource|false', 'broker'=>'resource', 'tag'=>'string'], 'enchant_broker_request_pwl_dict' => ['resource|false', 'broker'=>'resource', 'filename'=>'string'], 'enchant_broker_set_dict_path' => ['bool', 'broker'=>'resource', 'type'=>'int', 'path'=>'string'], @@ -10579,7 +10579,7 @@ 'enchant_dict_suggest' => ['array', 'dictionary'=>'resource', 'word'=>'string'], 'end' => ['mixed|false', '&r_array'=>'array|object'], 'error_clear_last' => ['void'], - 'error_get_last' => ['?array{type:int,message:string,file:string,line:int}'], + 'error_get_last' => ['?strict-array{type:int,message:string,file:string,line:int}'], 'error_log' => ['bool', 'message'=>'string', 'message_type='=>'int', 'destination='=>'string', 'additional_headers='=>'string'], 'error_reporting' => ['int', 'error_level='=>'int'], 'escapeshellarg' => ['string', 'arg'=>'string'], @@ -10918,7 +10918,7 @@ 'ffmpeg_movie::hasAudio' => ['bool'], 'ffmpeg_movie::hasVideo' => ['bool'], 'fgetc' => ['string|false', 'stream'=>'resource'], - 'fgetcsv' => ['list|array{0: null}|false|null', 'stream'=>'resource', 'length='=>'int', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], + 'fgetcsv' => ['list|strict-array{0: null}|false|null', 'stream'=>'resource', 'length='=>'int', 'separator='=>'string', 'enclosure='=>'string', 'escape='=>'string'], 'fgets' => ['string|false', 'stream'=>'resource', 'length='=>'int'], 'fgetss' => ['string|false', 'fp'=>'resource', 'length='=>'int', 'allowable_tags='=>'string'], 'file' => ['list|false', 'filename'=>'string', 'flags='=>'int', 'context='=>'resource'], @@ -10978,7 +10978,7 @@ 'fscanf\'1' => ['int', 'stream'=>'resource', 'format'=>'string', '&...w_vars='=>'string|int|float'], 'fseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], 'fsockopen' => ['resource|false', 'hostname'=>'string', 'port='=>'int', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float'], - 'fstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'stream'=>'resource'], + 'fstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'stream'=>'resource'], 'ftell' => ['int|false', 'stream'=>'resource'], 'ftok' => ['int', 'filename'=>'string', 'project_id'=>'string'], 'ftp_alloc' => ['bool', 'ftp'=>'resource', 'size'=>'int', '&w_response='=>'string'], @@ -11170,7 +11170,7 @@ 'get_resources' => ['array', 'type='=>'string'], 'getallheaders' => ['array|false'], 'getcwd' => ['string|false'], - 'getdate' => ['array{seconds: int<0, 59>, minutes: int<0, 59>, hours: int<0, 23>, mday: int<1, 31>, wday: int<0, 6>, mon: int<1, 12>, year: int, yday: int<0, 365>, weekday: "Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday", month: "January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December", 0: int}', 'timestamp='=>'int'], + 'getdate' => ['strict-array{seconds: int<0, 59>, minutes: int<0, 59>, hours: int<0, 23>, mday: int<1, 31>, wday: int<0, 6>, mon: int<1, 12>, year: int, yday: int<0, 365>, weekday: "Monday"|"Tuesday"|"Wednesday"|"Thursday"|"Friday"|"Saturday"|"Sunday", month: "January"|"February"|"March"|"April"|"May"|"June"|"July"|"August"|"September"|"October"|"November"|"December", 0: int}', 'timestamp='=>'int'], 'getenv' => ['string|false', 'name'=>'string', 'local_only='=>'bool'], 'gethostbyaddr' => ['string|false', 'ip'=>'string'], 'gethostbyname' => ['string', 'hostname'=>'string'], @@ -11206,7 +11206,7 @@ 'gmp_com' => ['GMP', 'num'=>'GMP|string|int'], 'gmp_div' => ['GMP', 'num1'=>'GMP|resource|string', 'num2'=>'GMP|resource|string', 'rounding_mode='=>'int'], 'gmp_div_q' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int', 'rounding_mode='=>'int'], - 'gmp_div_qr' => ['array{0: GMP, 1: GMP}', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int', 'rounding_mode='=>'int'], + 'gmp_div_qr' => ['strict-array{0: GMP, 1: GMP}', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int', 'rounding_mode='=>'int'], 'gmp_div_r' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int', 'rounding_mode='=>'int'], 'gmp_divexact' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_export' => ['string|false', 'num'=>'GMP|string|int', 'word_size='=>'int', 'flags='=>'int'], @@ -11235,13 +11235,13 @@ 'gmp_random_range' => ['GMP', 'min'=>'GMP|string|int', 'max'=>'GMP|string|int'], 'gmp_random_seed' => ['void', 'seed'=>'GMP|string|int'], 'gmp_root' => ['GMP', 'num'=>'GMP|string|int', 'nth'=>'int'], - 'gmp_rootrem' => ['array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int', 'nth'=>'int'], + 'gmp_rootrem' => ['strict-array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int', 'nth'=>'int'], 'gmp_scan0' => ['int', 'num1'=>'GMP|string|int', 'start'=>'int'], 'gmp_scan1' => ['int', 'num1'=>'GMP|string|int', 'start'=>'int'], 'gmp_setbit' => ['void', 'num'=>'GMP|string|int', 'index'=>'int', 'value='=>'bool'], 'gmp_sign' => ['int', 'num'=>'GMP|string|int'], 'gmp_sqrt' => ['GMP', 'num'=>'GMP|string|int'], - 'gmp_sqrtrem' => ['array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int'], + 'gmp_sqrtrem' => ['strict-array{0: GMP, 1: GMP}', 'num'=>'GMP|string|int'], 'gmp_strval' => ['numeric-string', 'num'=>'GMP|string|int', 'base='=>'int'], 'gmp_sub' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], 'gmp_testbit' => ['bool', 'num'=>'GMP|string|int', 'index'=>'int'], @@ -12119,8 +12119,8 @@ 'image_type_to_extension' => ['string', 'image_type'=>'int', 'include_dot='=>'bool'], 'image_type_to_mime_type' => ['string', 'image_type'=>'int'], 'imageaffine' => ['resource|false', 'src'=>'resource', 'affine'=>'array', 'clip='=>'array'], - 'imageaffinematrixconcat' => ['array{0:float,1:float,2:float,3:float,4:float,5:float}|false', 'matrix1'=>'array', 'matrix2'=>'array'], - 'imageaffinematrixget' => ['array{0:float,1:float,2:float,3:float,4:float,5:float}|false', 'type'=>'int', 'options'=>'array|float'], + 'imageaffinematrixconcat' => ['strict-array{0:float,1:float,2:float,3:float,4:float,5:float}|false', 'matrix1'=>'array', 'matrix2'=>'array'], + 'imageaffinematrixget' => ['strict-array{0:float,1:float,2:float,3:float,4:float,5:float}|false', 'type'=>'int', 'options'=>'array|float'], 'imagealphablending' => ['bool', 'image'=>'resource', 'enable'=>'bool'], 'imageantialias' => ['bool', 'image'=>'resource', 'enable'=>'bool'], 'imagearc' => ['bool', 'image'=>'resource', 'center_x'=>'int', 'center_y'=>'int', 'width'=>'int', 'height'=>'int', 'start_angle'=>'int', 'end_angle'=>'int', 'color'=>'int'], @@ -12665,7 +12665,7 @@ 'log10' => ['float', 'num'=>'float'], 'log1p' => ['float', 'num'=>'float'], 'long2ip' => ['string', 'ip'=>'string|int'], - 'lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], + 'lstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'ltrim' => ['string', 'string'=>'string', 'characters='=>'string'], 'lzf_compress' => ['string', 'data'=>'string'], 'lzf_decompress' => ['string', 'data'=>'string'], @@ -14464,17 +14464,17 @@ 'posix_getegid' => ['int'], 'posix_geteuid' => ['int'], 'posix_getgid' => ['int'], - 'posix_getgrgid' => ['array{name: string, passwd: string, gid: int, members: list}|false', 'group_id'=>'int'], - 'posix_getgrnam' => ['array{name: string, passwd: string, gid: int, members: list}|false', 'name'=>'string'], + 'posix_getgrgid' => ['strict-array{name: string, passwd: string, gid: int, members: list}|false', 'group_id'=>'int'], + 'posix_getgrnam' => ['strict-array{name: string, passwd: string, gid: int, members: list}|false', 'name'=>'string'], 'posix_getgroups' => ['list|false'], 'posix_getlogin' => ['string|false'], 'posix_getpgid' => ['int|false', 'process_id'=>'int'], 'posix_getpgrp' => ['int'], 'posix_getpid' => ['int'], 'posix_getppid' => ['int'], - 'posix_getpwnam' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'username'=>'string'], - 'posix_getpwuid' => ['array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'user_id'=>'int'], - 'posix_getrlimit' => ['array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'], + 'posix_getpwnam' => ['strict-array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'username'=>'string'], + 'posix_getpwuid' => ['strict-array{name: string, passwd: string, uid: int, gid: int, gecos: string, dir: string, shell: string}|false', 'user_id'=>'int'], + 'posix_getrlimit' => ['strict-array{"soft core": string, "hard core": string, "soft data": string, "hard data": string, "soft stack": integer, "hard stack": string, "soft totalmem": string, "hard totalmem": string, "soft rss": string, "hard rss": string, "soft maxproc": integer, "hard maxproc": integer, "soft memlock": integer, "hard memlock": integer, "soft cpu": string, "hard cpu": string, "soft filesize": string, "hard filesize": string, "soft openfiles": integer, "hard openfiles": integer}|false'], 'posix_getsid' => ['int|false', 'process_id'=>'int'], 'posix_getuid' => ['int'], 'posix_initgroups' => ['bool', 'username'=>'string', 'group_id'=>'int'], @@ -14490,9 +14490,9 @@ 'posix_setsid' => ['int'], 'posix_setuid' => ['bool', 'user_id'=>'int'], 'posix_strerror' => ['string', 'error_code'=>'int'], - 'posix_times' => ['array{ticks: int, utime: int, stime: int, cutime: int, cstime: int}|false'], + 'posix_times' => ['strict-array{ticks: int, utime: int, stime: int, cutime: int, cstime: int}|false'], 'posix_ttyname' => ['string|false', 'file_descriptor'=>'resource|int'], - 'posix_uname' => ['array{sysname: string, nodename: string, release: string, version: string, machine: string, domainname: string}|false'], + 'posix_uname' => ['strict-array{sysname: string, nodename: string, release: string, version: string, machine: string, domainname: string}|false'], 'pow' => ['float|int', 'num'=>'int|float', 'exponent'=>'int|float'], 'preg_filter' => ['null|string|string[]', 'pattern'=>'mixed', 'replacement'=>'mixed', 'subject'=>'mixed', 'limit='=>'int', '&w_count='=>'int'], 'preg_grep' => ['array|false', 'pattern'=>'string', 'array'=>'array', 'flags='=>'int'], @@ -14513,7 +14513,7 @@ 'print_r\'1' => ['true', 'value'=>'mixed', 'return='=>'bool'], 'printf' => ['int', 'format'=>'string', '...values='=>'string|int|float'], 'proc_close' => ['int', 'process'=>'resource'], - 'proc_get_status' => ['array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}|false', 'process'=>'resource'], + 'proc_get_status' => ['strict-array{command: string, pid: int, running: bool, signaled: bool, stopped: bool, exitcode: int, termsig: int, stopsig: int}|false', 'process'=>'resource'], 'proc_nice' => ['bool', 'priority'=>'int'], 'proc_open' => ['resource|false', 'command'=>'string', 'descriptor_spec'=>'array', '&pipes'=>'resource[]', 'cwd='=>'?string', 'env_vars='=>'?array', 'options='=>'?array'], 'proc_terminate' => ['bool', 'process'=>'resource', 'signal='=>'int'], @@ -15119,18 +15119,18 @@ 'ssh2_scp_send' => ['bool', 'session'=>'resource', 'local_file'=>'string', 'remote_file'=>'string', 'create_mode='=>'int'], 'ssh2_sftp' => ['resource|false', 'session'=>'resource'], 'ssh2_sftp_chmod' => ['bool', 'sftp'=>'resource', 'filename'=>'string', 'mode'=>'int'], - 'ssh2_sftp_lstat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], + 'ssh2_sftp_lstat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], 'ssh2_sftp_mkdir' => ['bool', 'sftp'=>'resource', 'dirname'=>'string', 'mode='=>'int', 'recursive='=>'bool'], 'ssh2_sftp_readlink' => ['string|false', 'sftp'=>'resource', 'link'=>'string'], 'ssh2_sftp_realpath' => ['string|false', 'sftp'=>'resource', 'filename'=>'string'], 'ssh2_sftp_rename' => ['bool', 'sftp'=>'resource', 'from'=>'string', 'to'=>'string'], 'ssh2_sftp_rmdir' => ['bool', 'sftp'=>'resource', 'dirname'=>'string'], - 'ssh2_sftp_stat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], + 'ssh2_sftp_stat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'sftp'=>'resource', 'path'=>'string'], 'ssh2_sftp_symlink' => ['bool', 'sftp'=>'resource', 'target'=>'string', 'link'=>'string'], 'ssh2_sftp_unlink' => ['bool', 'sftp'=>'resource', 'filename'=>'string'], 'ssh2_shell' => ['resource|false', 'session'=>'resource', 'term_type='=>'string', 'env='=>'array', 'width='=>'int', 'height='=>'int', 'width_height_type='=>'int'], 'ssh2_tunnel' => ['resource|false', 'session'=>'resource', 'host'=>'string', 'port'=>'int'], - 'stat' => ['array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], + 'stat' => ['strict-array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int, 9: int, 10: int, 11: int, 12: int, dev: int, ino: int, mode: int, nlink: int, uid: int, gid: int, rdev: int, size: int, atime: int, mtime: int, ctime: int, blksize: int, blocks: int}|false', 'filename'=>'string'], 'stats_absolute_deviation' => ['float', 'a'=>'array'], 'stats_cdf_beta' => ['float', 'par1'=>'float', 'par2'=>'float', 'par3'=>'float', 'which'=>'int'], 'stats_cdf_binomial' => ['float', 'par1'=>'float', 'par2'=>'float', 'par3'=>'float', 'which'=>'int'], @@ -15282,7 +15282,7 @@ 'stream_get_contents' => ['string|false', 'stream'=>'resource', 'length='=>'int', 'offset='=>'int'], 'stream_get_filters' => ['array'], 'stream_get_line' => ['string|false', 'stream'=>'resource', 'length'=>'int', 'ending='=>'string'], - 'stream_get_meta_data' => ['array{timed_out:bool,blocked:bool,eof:bool,unread_bytes:int,stream_type:string,wrapper_type:string,wrapper_data:mixed,mode:string,seekable:bool,uri:string,mediatype:string,crypto?:array{protocol:string,cipher_name:string,cipher_bits:int,cipher_version:string}}', 'stream'=>'resource'], + 'stream_get_meta_data' => ['strict-array{timed_out:bool,blocked:bool,eof:bool,unread_bytes:int,stream_type:string,wrapper_type:string,wrapper_data:mixed,mode:string,seekable:bool,uri:string,mediatype:string,crypto?:strict-array{protocol:string,cipher_name:string,cipher_bits:int,cipher_version:string}}', 'stream'=>'resource'], 'stream_get_transports' => ['list'], 'stream_get_wrappers' => ['list'], 'stream_is_local' => ['bool', 'stream'=>'resource|string'], @@ -15805,19 +15805,19 @@ 'tidy_setopt' => ['bool', 'option'=>'string', 'value'=>'mixed'], 'tidy_warning_count' => ['int', 'tidy'=>'tidy'], 'time' => ['positive-int'], - 'time_nanosleep' => ['array{0:0|positive-int,1:0|positive-int}|bool', 'seconds'=>'positive-int', 'nanoseconds'=>'positive-int'], + 'time_nanosleep' => ['strict-array{0:0|positive-int,1:0|positive-int}|bool', 'seconds'=>'positive-int', 'nanoseconds'=>'positive-int'], 'time_sleep_until' => ['bool', 'timestamp'=>'float'], - 'timezone_abbreviations_list' => ['array>|false'], + 'timezone_abbreviations_list' => ['array>|false'], 'timezone_identifiers_list' => ['list|false', 'timezoneGroup='=>'int', 'countryCode='=>'string'], 'timezone_location_get' => ['array|false', 'object'=>'DateTimeZone'], 'timezone_name_from_abbr' => ['string|false', 'abbr'=>'string', 'utcOffset='=>'int', 'isDST='=>'int'], 'timezone_name_get' => ['string', 'object'=>'DateTimeZone'], 'timezone_offset_get' => ['int|false', 'object'=>'DateTimeZone', 'datetime'=>'DateTimeInterface'], 'timezone_open' => ['DateTimeZone|false', 'timezone'=>'string'], - 'timezone_transitions_get' => ['list|false', 'object'=>'DateTimeZone', 'timestampBegin='=>'int', 'timestampEnd='=>'int'], + 'timezone_transitions_get' => ['list|false', 'object'=>'DateTimeZone', 'timestampBegin='=>'int', 'timestampEnd='=>'int'], 'timezone_version_get' => ['string'], 'tmpfile' => ['resource|false'], - 'token_get_all' => ['list', 'code'=>'string', 'flags='=>'int'], + 'token_get_all' => ['list', 'code'=>'string', 'flags='=>'int'], 'token_name' => ['string', 'id'=>'int'], 'touch' => ['bool', 'filename'=>'string', 'mtime='=>'int', 'atime='=>'int'], 'trader_acos' => ['array', 'real'=>'array'], diff --git a/docs/annotating_code/supported_annotations.md b/docs/annotating_code/supported_annotations.md index 5393cca2e7a..67c7542cc3d 100644 --- a/docs/annotating_code/supported_annotations.md +++ b/docs/annotating_code/supported_annotations.md @@ -484,7 +484,7 @@ This allows you to define an alias for another type. ```php 2) { + echo $arr['baz']; // OK! +} + +// Off by one bug, detected by Psalm: +if (count($arr) > 3) { + // DocblockTypeContradiction - Docblock-defined type for $arr is never has-at-least-4 +} + +/** @param strict-array{0: float, 1: float, 2?: float} $arr */ +function avgShape(array $arr): float { + return array_sum($arr) / count($arr); +} + +// InvalidArgument - Argument 1 of avgShape expects ... +avgShape([123.1, 321.0, 1.0, new class {}, 'test']); +``` + +The above examples contain bugs which can be detected by Psalm *only when using sealed arrays*. + +The counterpart to sealed arrays are [unsealed arrays &rauo;](#unsealed-object-like-arrays), generated as intermediate types when asserting raw arrays. +Unsealed arrays are by definition uncertain, so Psalm can't reason well about them: always convert them to sealed arrays as specified [here »](#unsealed-object-like-arrays). + +Tip: if you find yourself copying the same complex sealed array shape over and over again to avoid `InvalidArgument` issues, try using [type aliases](utility_types.md#type-aliases), instead. + +### Unsealed object-like arrays + +Starting from v5, Psalm defines a supertype of object-like arrays called unsealed object-like arrays. +This type is used in cases where an [object-like array](#object-like-arrays) may have extra keys not specified in the shape. +Avoid using unsealed arrays in your codebase, as **they can cause undetectable bugs**: always transform them into sealed arrays before use. +Unsealed arrays are by definition uncertain, so, unlike [sealed arrays](#sealed-object-like-arrays), Psalm can't reason well about them: always use sealed arrays in your PHPDocs. + +Here's how unsealed arrays can cause weird bugs: + +```php + $arr['a'], 'b' => $arr['b']]`, but there's an even better way to seamlessly validate user-provided input: + + + +Tip: if you find yourself copying the same complex sealed array shape over and over again to avoid `InvalidArgument` issues, try using [type aliases](utility_types.md#type-aliases), instead. + ## Callable arrays An array holding a callable, like PHP's native `call_user_func()` and friends supports it: @@ -135,3 +314,4 @@ $callable = [$object, 'aMethod']; ## non-empty-array An array which is not allowed to be empty. +[Generic syntax](#generic-arrays) is also supported: `non-empty-array`. \ No newline at end of file diff --git a/docs/annotating_code/type_syntax/atomic_types.md b/docs/annotating_code/type_syntax/atomic_types.md index 911e9a39565..cfe64147397 100644 --- a/docs/annotating_code/type_syntax/atomic_types.md +++ b/docs/annotating_code/type_syntax/atomic_types.md @@ -2,76 +2,61 @@ Atomic types are the basic building block of all type information used in Psalm. Multiple atomic types can be combined, either with [union types](union_types.md) or [intersection types](intersection_types.md). Psalm allows many different sorts of atomic types to be expressed in docblock syntax: -## [Scalar types](scalar_types.md) - -- [int](scalar_types.md) -- [positive-int](scalar_types.md#positive-int) -- [float](scalar_types.md) -- [string](scalar_types.md) -- [class-string and class-string<Foo>](scalar_types.md#class-string-interface-string) -- [trait-string](scalar_types.md#trait-string) -- [enum-string](scalar_types.md#enum-string) -- [callable-string](scalar_types.md#callable-string) -- [numeric-string](scalar_types.md#numeric-string) -- [literal-string](scalar_types.md#literal-string) -- [bool](scalar_types.md) -- [array-key](scalar_types.md#array-key) -- [numeric](scalar_types.md#numeric) -- [scalar](scalar_types.md#scalar) - -## [Object types](object_types.md) - -- [object](object_types.md) -- [object{foo: string}](object_types.md) -- [Exception, Foo\MyClass and Foo\MyClass](object_types.md) -- [Generator](object_types.md) - -## [Array types](array_types.md) - -- [array & non-empty-array](array_types.md) -- [array<int, string>](array_types.md#generic-arrays) -- [string\[\]](array_types.md#phpdoc-syntax) -- [list & non-empty-list](array_types.md#lists) -- [list<string>](array_types.md#lists) -- [array{foo: int, bar: string}](array_types.md#object-like-arrays) -- [callable-array](array_types.md#callable-array) - -## [Callable types](callable_types.md) - -- [callable, Closure and callable(Foo, Bar):Baz](callable_types.md) - -## [Value types](value_types.md) - -- [null](value_types.md#null) -- [true, false](value_types.md#true-false) -- [6, 7.0, "forty-two" and 'forty two'](value_types.md#some_string-4-314) -- [Foo\Bar::MY_SCALAR_CONST](value_types.md#regular-class-constants) - -## Magical types - -- [(T is true ? string : bool)](conditional_types.md) -- [`key-of`](utility_types.md#key-oft) -- [`value-of`](utility_types.md#value-oft) -- `T[K]` -- [`properties-of`](utility_types.md#properties-oft) - -## 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 `reportMixedIssues` 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. 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 - -- `iterable` - represents the [iterable pseudo-type](https://php.net/manual/en/language.types.iterable.php). Like arrays, iterables can have type parameters e.g. `iterable`. -- `void` - can be used in a return type when a function does not return a value. -- `resource` represents a [PHP resource](https://www.php.net/manual/en/language.types.resource.php). +_Click on the » next to each type to view detailed documentation and examples._ + +* [Scalar types »](scalar_types.md) + * [bool »](scalar_types.md#scalar) + * [int »](scalar_types.md#scalar) + * [float »](scalar_types.md#scalar) + * [string »](scalar_types.md#scalar) + * [`int-range` »](scalar_types.md#int-range) + * [`int-mask<1, 2, 4>` »](scalar_types.md#int-mask1-2-4) + * [`int-mask-of` »](scalar_types.md#int-mask-ofmyclassclass_constant_) + * [class-string and class-string<Foo> »](scalar_types.md#class-string-interface-string) + * [trait-string »](scalar_types.md#trait-string) + * [enum-string »](scalar_types.md#enum-string) + * [callable-string »](scalar_types.md#callable-string) + * [numeric-string »](scalar_types.md#numeric-string) + * [literal-string »](scalar_types.md#literal-string) + * [literal-int »](scalar_types.md#literal-int) + * [array-key »](scalar_types.md#array-key) + * [numeric »](scalar_types.md#numeric) + * [scalar »](scalar_types.md#scalar) +* [Object types »](object_types.md) + * [object »](object_types.md#unnamed-objects) + * [object{foo: string} »](object_types.md#object-properties) + * [Exception, Foo\MyClass and `Foo\MyClass` »](object_types.md#named-objectsmd) + * [Generator](object_types.md#generators) +* [Array types »](array_types.md) + * [array<int, string> »](array_types.md#generic-arrays) + * [non-empty-array »](array_types.md#non-empty-array) + * [string\[\] »](array_types.md#phpdoc-syntax) + * [list & non-empty-list »](array_types.md#lists) + * [list<string> »](array_types.md#lists) + * [array{foo: int, bar: string} and list{int, string} »](array_types.md#object-like-arrays) + * [Sealed arrays »](array_types.md#sealed-object-like-arrays) + * [Unsealed arrays »](array_types.md#unsealed-object-like-arrays) + * [callable-array »](array_types.md#callable-arrays) +* [Callable types »](callable_types.md) +* [Value types »](value_types.md) + * [null »](value_types.md#null) + * [true, false »](value_types.md#true-false) + * [6, 7.0, "forty-two" and 'forty two' »](value_types.md#some_string-4-314) + * [Foo\Bar::MY_SCALAR_CONST »](value_types.md#regular-class-constants) +* [Utility types »](utility_types.md) + * [(T is true ? string : bool) »](conditional_types.md) + * [`key-of` »](utility_types.md#key-oft) + * [`value-of` »](utility_types.md#value-oft) + * [`properties-of` »](utility_types.md#properties-oft) + * [`class-string-map` »](utility_types.md#class-string-mapt-as-foo-t) + * [`T[K]` »](utility_types.md#tk) + * [Type aliases »](utility_types.md#type-aliases) + * [Variable templates »](utility_types.md#variable-templates) +* [Other types »](other_types.md) + * [`iterable` »](other_types.md) + * [void »](other_types.md) + * [resource »](other_types.md) + * [closed-resource »](other_types.md) +* [Top and bottom types »](top_bottom_types.md) + * [mixed »](top_bottom_types.md#mixed) + * [never »](top_bottom_types.md#never) diff --git a/docs/annotating_code/type_syntax/intersection_types.md b/docs/annotating_code/type_syntax/intersection_types.md index e7a94dc2311..299bd7e791c 100644 --- a/docs/annotating_code/type_syntax/intersection_types.md +++ b/docs/annotating_code/type_syntax/intersection_types.md @@ -15,8 +15,8 @@ required to implement multiple interfaces. Another use case is being able to merge object-like arrays: ```php /** - * @psalm-type A=array{a: int} - * @psalm-type B=array{b: int} + * @psalm-type A=strict-array{a: int} + * @psalm-type B=strict-array{b: int} * * @param A $a * @param B $b diff --git a/docs/annotating_code/type_syntax/object_types.md b/docs/annotating_code/type_syntax/object_types.md index 18de65d71a3..eca8397ae54 100644 --- a/docs/annotating_code/type_syntax/object_types.md +++ b/docs/annotating_code/type_syntax/object_types.md @@ -1,8 +1,19 @@ # Object types -`object`, `stdClass`, `Foo`, `Bar\Baz` etc. are examples of object types. These types are also valid types in PHP. +- [object](#unnamed-objects) +- [object{foo: string}](#object-properties) +- [Exception, Foo\MyClass and Foo\MyClass](#named-objectsmd) +- [Generator](#generators) -#### Object properties +### Unnamed objects + +`object` are examples of unnamed object types. This type is also a valid type in PHP. + +### Named objects + +`stdClass`, `Foo`, `Bar\Baz` etc. are examples of named object types. These types are also valid types in PHP. + +### Object properties Psalm supports specifying the properties of an object and their expected types, e.g.: @@ -21,11 +32,11 @@ Optional properties can be denoted by a trailing `?`, e.g.: /** @param object{optional?: string} */ ``` -#### Generic object types +### Generic object types Psalm supports using generic object types like `ArrayObject`. Any generic object should be typehinted with appropriate [`@template` tags](../templated_annotations.md). -#### Generators +### Generators Generator types support up to four parameters, e.g. `Generator`: diff --git a/docs/annotating_code/type_syntax/scalar_types.md b/docs/annotating_code/type_syntax/scalar_types.md index a59b1be56ae..92e8d69d317 100644 --- a/docs/annotating_code/type_syntax/scalar_types.md +++ b/docs/annotating_code/type_syntax/scalar_types.md @@ -1,18 +1,57 @@ # Scalar types -`int`, `bool`, `float`, `string` are examples of scalar types. Scalar types represent scalar values in PHP. These types are also valid types in PHP 7. +* [bool »](scalar_types.md#scalar) +* [int »](scalar_types.md#scalar) +* [float »](scalar_types.md#scalar) +* [string »](scalar_types.md#scalar) +* [`int-range` »](scalar_types.md#int-range) +* [`int-mask<1, 2, 4>` »](scalar_types.md#int-mask1-2-4) +* [`int-mask-of` »](scalar_types.md#int-mask-ofmyclassclass_constant_) +* [class-string and class-string<Foo> »](scalar_types.md#class-string-interface-string) +* [trait-string »](scalar_types.md#trait-string) +* [enum-string »](scalar_types.md#enum-string) +* [callable-string »](scalar_types.md#callable-string) +* [numeric-string »](scalar_types.md#numeric-string) +* [literal-string »](scalar_types.md#literal-string) +* [literal-int »](scalar_types.md#literal-int) +* [array-key »](scalar_types.md#array-key) +* [numeric »](scalar_types.md#numeric) +* [scalar »](scalar_types.md#scalar) ### scalar +`int`, `bool`, `float`, `string` are examples of scalar types. Scalar types represent scalar values in PHP. These types are also valid types in PHP 7. The type `scalar` is the supertype of all scalar types. -### array-key +### int-range -`array-key` is the supertype (but not a union) of `int` and `string`. +Integer ranges indicate an integer within a range, specified using generic syntax: `int`. +`x` and `y` must be integer numbers. +`x` can also be `min` to indicate PHP_INT_MIN, and `y` can be `max` to indicate PHP_INT_MAX. + +Examples: + +* `int<-1, 3>` +* `int` +* `int<1, max>` (equivalent to `positive-int`) +* `int<0, max>` (equivalent to `non-negative-int`) +* `int` (equivalent to `negative-int`) +* `int` (equivalent to `non-positive-int`) +* `int` (equivalent to `int`) + +## `int-mask<1, 2, 4>` + +Represents the type that is the result of a bitmask combination of its parameters. +`int-mask<1, 2, 4>` corresponds to `0|1|2|3|4|5|6|7`. + +## `int-mask-of` -### positive-int +Represents the type that is the result of a bitmask combination of its parameters. +This is the same concept as [`int-mask`](#int-mask1-2-4) but this type is used with a reference to constants in code: `int-mask-of` will correspond to `0|1|2|3|4|5|6|7` if there are three constants called `CLASS_CONSTANT_{A,B,C}` with values 1, 2 and 4. -`positive-int` allows only positive integers (equivalent to `int<1, max>`) +### array-key + +`array-key` is the supertype (but not a union) of `int` and `string`. ### numeric @@ -72,6 +111,21 @@ Strings that don't pass this type check: - `$_GET["foo"]` - `"hello " . $_GET["foo"]` +### literal-int + +`literal-int` denotes an int value that is entirely composed of literal integers in your application. + +Examples: + +- `12` +- `12+42` + +Integers that don't pass this type check: + +- `(int) file_get_contents("foo.txt")` +- `(int) $_GET["foo"]` +- `((int)$_GET["foo"]) + 2` + ### lowercase-string, non-empty-string, non-empty-lowercase-string A non empty string, lowercased or both at once. diff --git a/docs/annotating_code/type_syntax/utility_types.md b/docs/annotating_code/type_syntax/utility_types.md index b85ea3b00a5..8b3fef940cc 100644 --- a/docs/annotating_code/type_syntax/utility_types.md +++ b/docs/annotating_code/type_syntax/utility_types.md @@ -2,6 +2,15 @@ Psalm supports some _magical_ utility types that brings superpower to the PHP type system. +- [(T is true ? string : bool)](conditional_types.md) +- [`key-of`](#key-oft) +- [`value-of`](#value-oft) +- [`properties-of`](#properties-oft) +- [`class-string-map`](#class-string-mapt-as-foo-t) +- [`T[K]`](#tk) +- [Type aliases](#type-aliases) +- [Variable templates](#variable-templates) + ## `key-of` (Psalm 5.0+) @@ -11,7 +20,7 @@ The `key-of` utility returns the offset-type for any [array type](array_types.md Some examples: - `key-of` evaluates to offset-type of `ARRAY_CONST` (Psalm 3.3+) - `key-of>` evaluates to `int` -- `key-of` evaluates to `'a'|'b'|'c'` +- `key-of` evaluates to `'a'|'b'|'c'` - `key-of` evaluates to `array-key` - `key-of` evaluates to the template param's offset-type (ensure `@template T of array`) @@ -45,7 +54,7 @@ The `value-of` utility returns the value-type for any [array type](array_types.m Some examples: - `value-of` evaluates to value-type of `ARRAY_CONST` (Psalm 3.3+) - `value-of>` evaluates to `float` -- `value-of` evaluates to `bool|int|string` +- `value-of` evaluates to `bool|int|string` - `value-of` evaluates to `string` - `value-of` evaluates to the template param's value-type (ensure `@template T of array`) @@ -110,18 +119,16 @@ properties with a certain visibility: ### Limited template support -As there is no way to statically analyze if a method returns all properties of a generic param (e.g. via Reflection or -serialization), you have to annotate it where you assume it. +Use final classes if you want to properties-of and get_object_vars to return [sealed arrays](array_types.md#sealed-object-like-arrays): ```php /** + * @template T * @param T $object * @return properties-of */ -public function asArray($object): array { - /** @var properties-of */ - $array = json_decode(json_encode($object), true); - return $array; +function asArray($object): array { + return get_object_vars($object); } @@ -130,8 +137,228 @@ class A { public int $bar = 42; } -$a = new A(); -$aAsArray = asArray($a); -$aAsArray['foo']; // valid -$aAsArray['adams']; // error! +final class B extends A { + public float $baz = 2.1; +} + +$a = asArray(new A); +/** @psalm-trace $a */; // unsealed-strict-array{foo: string, bar: int} + +$b = asArray(new B); +/** @psalm-trace $b */; // strict-array{foo: string, bar: int, baz: float} +``` + +## `class-string-map` + +Used to indicate an array where each value is equal an instance of the class string contained in the key: + +```php + */ + private static array $map = []; + + /** + * @template U as Foo + * @param class-string $class + * @return U + */ + public static function get(string $class) : Foo { + if (isset(self::$map[$class])) { + return self::$map[$class]; + } + + self::$map[$class] = new $class(); + return self::$map[$class]; + } +} + +$foo = A::get(Foo::class); +$bar = A::get(Bar::class); + +/** @psalm-trace $foo */; // Foo +/** @psalm-trace $bar */; // Bar +``` + +If we had used an `array, Foo>` instead of a `class-string-map` in the above example, we would've gotten some false positive `InvalidReturnStatement` issues, caused by the lack of a type assertion inside the `isset`. +On the other hand, when using `class-string-map`, Psalm assumes that the value obtained by using a key `class-string` is always equal to `T`. + +Unbounded templates can also be used for unrelated classes: + +```php + */ + private static array $map = []; + + /** + * @template U + * @param class-string $class + * @return U + */ + public static function get(string $class) : object { + if (isset(self::$map[$class])) { + return self::$map[$class]; + } + + self::$map[$class] = new $class(); + return self::$map[$class]; + } +} + +$foo = A::get(Foo::class); +$bar = A::get(Bar::class); +$baz = A::get(Baz::class); + +/** @psalm-trace $foo */; // Foo +/** @psalm-trace $bar */; // Bar +/** @psalm-trace $baz */; // Baz +``` + +## `T[K]` + +Used to get the value corresponding to the specified key: + +```php + 123], 'test'); +/** @psalm-trace $a */; // 123 +``` + +## Type aliases + +Psalm allows defining type aliases for complex types (like array shapes) which must be reused often: + +```php +/** + * @psalm-type PhoneType = strict-array{phone: string} + */ +class Phone { + /** + * @psalm-return PhoneType + */ + public function toArray(): array { + return ["phone" => "Nokia"]; + } +} +``` + +You can use the [`@psalm-import-type`](../supported_annotations.md#psalm-import-type) annotation to import a type defined with [`@psalm-type`](../supported_annotations.md#psalm-type) if it was defined somewhere else. + +```php +toArray()); + } +} +``` + +You can also alias a type when you import it: + +```php +toArray()); + } +} +``` + +## Variable templates + +Variable templates allow directly using variables instead of template types, for example instead of the following verbose example: + +```php + ``` -This setting controls the maximum size of shaped arrays that will be transformed into a shaped `array{key1: "value", key2: T}` type during Psalm analysis. +This setting controls the maximum size of shaped arrays that will be transformed into a shaped `strict-array{key1: "value", key2: T}` type during Psalm analysis. Arrays bigger than this value (100 by default) will be transformed in a generic `non-empty-array` type, instead. Please note that changing this setting might introduce unwanted side effects and those side effects won't be considered as bugs. @@ -524,8 +524,8 @@ The following configuration declares custom types for super-globals (`$GLOBALS` ```xml - - + + ``` @@ -545,6 +545,6 @@ Plugins can access or modify the global configuration in plugins using ```php $config = \Psalm\Config::getInstance(); if (!isset($config->globals['$GLOBALS'])) { - $config->globals['$GLOBALS'] = 'array{data: array}'; + $config->globals['$GLOBALS'] = 'strict-array{data: array}'; } ``` diff --git a/docs/running_psalm/issues/RedundantFunctionCallGivenDocblockType.md b/docs/running_psalm/issues/RedundantFunctionCallGivenDocblockType.md index 7689d47f138..da54d474346 100644 --- a/docs/running_psalm/issues/RedundantFunctionCallGivenDocblockType.md +++ b/docs/running_psalm/issues/RedundantFunctionCallGivenDocblockType.md @@ -8,7 +8,7 @@ This may be desired (e.g. when checking user input) so is distinct from Redundan } $s + * @param strict-array{0: lowercase-string, 1: non-empty-list} $s * * @return lowercase-string */ diff --git a/docs/running_psalm/plugins/plugins_type_system.md b/docs/running_psalm/plugins/plugins_type_system.md index fcddb74e004..087f70db1c1 100644 --- a/docs/running_psalm/plugins/plugins_type_system.md +++ b/docs/running_psalm/plugins/plugins_type_system.md @@ -172,8 +172,8 @@ if (true === $first) { `TKeyedArray` represents an 'object-like array' - an array with known keys. ``` php -$x = ["a" => 1, "b" => 2]; // is TKeyedArray, array{a: int, b: int} -$y = rand(0, 1) ? ["a" => null] : ["a" => 1, "b" => "b"]; // is TKeyedArray with optional keys/values, array{a: ?int, b?: string} +$x = ["a" => 1, "b" => 2]; // is TKeyedArray, strict-array{a: int, b: int} +$y = rand(0, 1) ? ["a" => null] : ["a" => 1, "b" => "b"]; // is TKeyedArray with optional keys/values, strict-array{a: ?int, b?: string} ``` Note that not all associative arrays are considered object-like. If the keys are not known, the array is treated as a mapping between two types. diff --git a/src/Psalm/CodeLocation.php b/src/Psalm/CodeLocation.php index 664827f94ac..aba8dd37caa 100644 --- a/src/Psalm/CodeLocation.php +++ b/src/Psalm/CodeLocation.php @@ -387,7 +387,7 @@ public function getEndColumn(): int } /** - * @return array{0: int, 1: int} + * @return strict-array{0: int, 1: int} */ public function getSelectionBounds(): array { @@ -397,7 +397,7 @@ public function getSelectionBounds(): array } /** - * @return array{0: int, 1: int} + * @return strict-array{0: int, 1: int} */ public function getSnippetBounds(): array { diff --git a/src/Psalm/Codebase.php b/src/Psalm/Codebase.php index bc93e434df5..493d11580e6 100644 --- a/src/Psalm/Codebase.php +++ b/src/Psalm/Codebase.php @@ -991,7 +991,7 @@ public function getFunctionStorageForSymbol(string $file_path, string $symbol): /** * @param string $file_path * @param string $symbol - * @return array{ type: string, description?: string|null}|null + * @return strict-array{ type: string, description?: string|null}|null */ public function getSymbolInformation(string $file_path, string $symbol): ?array { @@ -1204,7 +1204,7 @@ public function getSymbolLocation(string $file_path, string $symbol): ?CodeLocat } /** - * @return array{0: string, 1: Range}|null + * @return strict-array{0: string, 1: Range}|null */ public function getReferenceAtPosition(string $file_path, Position $position): ?array { @@ -1257,7 +1257,7 @@ public function getReferenceAtPosition(string $file_path, Position $position): ? } /** - * @return array{0: non-empty-string, 1: int, 2: Range}|null + * @return strict-array{0: non-empty-string, 1: int, 2: Range}|null */ public function getFunctionArgumentAtPosition(string $file_path, Position $position): ?array { @@ -1393,7 +1393,7 @@ public function getSignatureInformation( } /** - * @return array{0: string, 1: '->'|'::'|'['|'symbol', 2: int}|null + * @return strict-array{0: string, 1: '->'|'::'|'['|'symbol', 2: int}|null */ public function getCompletionDataAtPosition(string $file_path, Position $position): ?array { @@ -1914,7 +1914,7 @@ public function canTypeBeContainedByType( * // returns [Union(TInt), Union(TString)] * ``` * - * @return array{Union, Union} + * @return strict-array{Union, Union} */ public function getKeyValueParamsForTraversableObject(Atomic $type): array { diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index c01a1b3a9ae..28ca03c8a90 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -491,7 +491,7 @@ class Config public $plugin_paths = []; /** - * @var array + * @var array */ private $plugin_classes = []; @@ -581,7 +581,7 @@ class Config /** * @psalm-readonly-allow-private-mutation - * @var array{ + * @var strict-array{ * decimal: bool, * dom: bool, * ds: bool, @@ -1425,7 +1425,7 @@ public function addPluginClass(string $class_name, ?SimpleXMLElement $plugin_con $this->plugin_classes[] = ['class' => $class_name, 'config' => $plugin_config]; } - /** @return array */ + /** @return array */ public function getPluginClasses(): array { return $this->plugin_classes; diff --git a/src/Psalm/Context.php b/src/Psalm/Context.php index fc9d283c864..86b823f2b2a 100644 --- a/src/Psalm/Context.php +++ b/src/Psalm/Context.php @@ -637,7 +637,7 @@ public function decrementReferenceCount(string $ref_id): void * @param Clause[] $clauses * @param array $changed_var_ids * - * @return array{list, list} + * @return strict-array{list, list} * * @psalm-pure */ diff --git a/src/Psalm/ErrorBaseline.php b/src/Psalm/ErrorBaseline.php index 84a804cdfe4..54135429001 100644 --- a/src/Psalm/ErrorBaseline.php +++ b/src/Psalm/ErrorBaseline.php @@ -33,7 +33,7 @@ final class ErrorBaseline { /** - * @param array}>> $existingIssues + * @param array}>> $existingIssues * * * @psalm-pure @@ -46,7 +46,7 @@ public static function countTotalIssues(array $existingIssues): int $totalIssues += array_reduce( $existingIssue, /** - * @param array{o:int, s:array} $existingIssue + * @param strict-array{o:int, s:array} $existingIssue */ static fn(int $carry, array $existingIssue): int => $carry + $existingIssue['o'], 0 @@ -72,7 +72,7 @@ public static function create( } /** - * @return array}>> + * @return array}>> * * @throws ConfigException */ @@ -134,7 +134,7 @@ public static function read(FileProvider $fileProvider, string $baselineFile): a /** * @param array> $issues * - * @return array}>> + * @return array}>> * * @throws ConfigException */ @@ -182,7 +182,7 @@ public static function update( /** * @param array> $issues * - * @return array}>> + * @return array}>> */ private static function countIssueTypesByFile(array $issues): array { @@ -192,9 +192,9 @@ private static function countIssueTypesByFile(array $issues): array $groupedIssues = array_reduce( array_merge(...array_values($issues)), /** - * @param array}>> $carry + * @param array}>> $carry * - * @return array}>> + * @return array}>> */ static function (array $carry, IssueData $issue): array { if ($issue->severity !== Config::REPORT_ERROR) { @@ -236,7 +236,7 @@ static function (array $carry, IssueData $issue): array { } /** - * @param array}>> $groupedIssues + * @param array}>> $groupedIssues * */ private static function writeToFile( diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 5d4f4ca2b08..98660f8e19c 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -1818,7 +1818,7 @@ public function getLocalReturnType(Union $storage_return_type, bool $final = fal } /** - * @return array{ + * @return strict-array{ * MethodIdentifier|null, * MethodIdentifier|null, * ClassLikeStorage|null, diff --git a/src/Psalm/Internal/Analyzer/IssueData.php b/src/Psalm/Internal/Analyzer/IssueData.php index 318afe57f6a..bc10343f41a 100644 --- a/src/Psalm/Internal/Analyzer/IssueData.php +++ b/src/Psalm/Internal/Analyzer/IssueData.php @@ -112,7 +112,7 @@ class IssueData public $link; /** - * @var ?list + * @var ?list */ public $taint_trace; @@ -128,7 +128,7 @@ class IssueData public $dupe_key; /** - * @param ?list $taint_trace + * @param ?list $taint_trace * @param ?list $other_references */ public function __construct( diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index 092e06cfa18..e8a03340dd1 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -470,7 +470,18 @@ public static function checkIteratorType( || $iterator_atomic_type instanceof TList ) { if ($iterator_atomic_type instanceof TKeyedArray) { - if (!$iterator_atomic_type->sealed) { + if ($iterator_atomic_type->sealed) { + $all_possibly_undefined = true; + foreach ($iterator_atomic_type->properties as $prop) { + if (!$prop->possibly_undefined) { + $all_possibly_undefined = false; + break; + } + } + if ($all_possibly_undefined) { + $always_non_empty_array = false; + } + } else { $always_non_empty_array = false; } $iterator_atomic_type = $iterator_atomic_type->getGenericArrayType(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php index fe4c759ecd0..acd3ca26f5e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php @@ -997,7 +997,7 @@ private static function getDimKeyValues( } /** - * @return array{TLiteralInt|TLiteralString|null, string, bool} + * @return strict-array{TLiteralInt|TLiteralString|null, string, bool} */ private static function getArrayAssignmentOffsetType( StatementsAnalyzer $statements_analyzer, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php index ff8c2058ff5..751b641397f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php @@ -66,7 +66,7 @@ public static function recordArgumentPositions( } // Record ranges of the source code that need to be tokenized to find commas - /** @var array{0: int, 1: int}[] $ranges */ + /** @var strict-array{0: int, 1: int}[] $ranges */ $ranges = []; // Add range between opening paren and first argument 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 aabb7498b03..e313124f14c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php @@ -513,7 +513,7 @@ public static function analyze( * @param TNamedObject|TTemplateParam $lhs_type_part * @param array $intersection_types * - * @return array{?Union, array} + * @return strict-array{?Union, array} */ private static function getIntersectionReturnType( StatementsAnalyzer $statements_analyzer, @@ -707,7 +707,7 @@ private static function handleInvalidClass( /** * @param lowercase-string $method_name_lc - * @return array{TNamedObject, ClassLikeStorage, bool, MethodIdentifier, string} + * @return strict-array{TNamedObject, ClassLikeStorage, bool, MethodIdentifier, string} */ private static function handleTemplatedMixins( ClassLikeStorage $class_storage, @@ -795,7 +795,7 @@ private static function handleTemplatedMixins( /** * @param lowercase-string $method_name_lc - * @return array{TNamedObject, ClassLikeStorage, bool, MethodIdentifier, string} + * @return strict-array{TNamedObject, ClassLikeStorage, bool, MethodIdentifier, string} */ private static function handleRegularMixins( ClassLikeStorage $class_storage, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php index 1982bcf707b..7d061c3f63e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php @@ -414,7 +414,7 @@ private static function createFirstClassCallableReturnType(?MethodStorage $metho * @param ClassLikeStorage $static_class_storage The called class * @param lowercase-string $method_name_lc * - * @return array{MethodStorage, ClassLikeStorage} + * @return strict-array{MethodStorage, ClassLikeStorage} */ private static function findPseudoMethodAndClassStorages( Codebase $codebase, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php index e6474f9baa6..181f1c579db 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php @@ -1083,7 +1083,7 @@ public static function handleNonObjectCall( * @param ClassLikeStorage $static_class_storage The called class * @param lowercase-string $method_name_lc * - * @return array{MethodStorage, ClassLikeStorage}|null + * @return strict-array{MethodStorage, ClassLikeStorage}|null */ private static function findPseudoMethodAndClassStorages( Codebase $codebase, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ClassConstAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ClassConstAnalyzer.php index 5e8db7bae83..7f0acae2910 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ClassConstAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ClassConstAnalyzer.php @@ -811,7 +811,7 @@ public static function analyze( /** * Get the const storage from the parent or interface that this class is overriding. * - * @return array{ClassLikeStorage, ClassConstantStorage}|null + * @return strict-array{ClassLikeStorage, ClassConstantStorage}|null */ private static function getOverriddenConstant( ClassLikeStorage $class_storage, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php index ffe867e5aa6..bcfab646aff 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php @@ -23,6 +23,7 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TBool; +use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TKeyedArray; @@ -170,7 +171,7 @@ public static function analyze( $type = $context->vars_in_scope[$var_name]; self::taintVariable($statements_analyzer, $var_name, $type, $stmt); - + $context->vars_in_scope[$var_name] = $type; $statements_analyzer->node_data->setType($stmt, $type); @@ -583,41 +584,37 @@ public static function getGlobalType(string $var_id, int $codebase_analysis_php_ if (isset(self::$globalCache[$var_id])) { return self::$globalCache[$var_id]; } - + return Type::getMixed(); } /** - * @psalm-suppress InaccessibleProperty Always acting on new types - * * @param value-of|'$argv'|'$argc' $var_id */ private static function getGlobalTypeInner(string $var_id, bool $files_full_path = false): Union { if ($var_id === '$argv') { // only in CLI, null otherwise - $argv_nullable = new Union([ + return new Union([ new TNonEmptyList(Type::getString()), new TNull() + ], [ + 'ignore_nullable_issues' => true ]); // use TNull explicitly instead of this // as it will cause weird errors due to ignore_nullable_issues true // e.g. InvalidPropertyAssignmentValue // $this->argv 'list' cannot be assigned type 'non-empty-list' - // $argv_nullable->possibly_undefined = true; - $argv_nullable->ignore_nullable_issues = true; - return $argv_nullable; } if ($var_id === '$argc') { // only in CLI, null otherwise - $argc_nullable = new Union([ + return new Union([ new TIntRange(1, null), new TNull() + ], [ + 'ignore_nullable_issues' => true ]); - // $argc_nullable->possibly_undefined = true; - $argc_nullable->ignore_nullable_issues = true; - return $argc_nullable; } if ($var_id === '$http_response_header') { @@ -672,118 +669,117 @@ private static function getGlobalTypeInner(string $var_id, bool $files_full_path } if ($var_id === '$_SERVER' || $var_id === '$_ENV') { - $string_helper = Type::getString(); - $string_helper->possibly_undefined = true; - - $non_empty_string_helper = Type::getNonEmptyString(); - $non_empty_string_helper->possibly_undefined = true; + $string_helper = new Union([new TString()], ['possibly_undefined' => true]); + $non_empty_string_helper = new Union([new TNonEmptyString()], ['possibly_undefined' => true]); $argv_helper = new Union([ new TNonEmptyList(Type::getString()) - ]); - $argv_helper->possibly_undefined = true; + ], ['possibly_undefined' => true]); $argc_helper = new Union([ new TIntRange(1, null) - ]); - $argc_helper->possibly_undefined = true; + ], ['possibly_undefined' => true]); $request_time_helper = new Union([ new TIntRange(time(), null) - ]); - $request_time_helper->possibly_undefined = true; - - $request_time_float_helper = Type::getFloat(); - $request_time_float_helper->possibly_undefined = true; + ], ['possibly_undefined' => true]); + + $request_time_float_helper = new Union([ + new TFloat() + ], ['possibly_undefined' => true]); + + $bool_string_helper = new Union([new TBool(), new TString()], ['possibly_undefined' => true]); + + $arr = [ + // https://www.php.net/manual/en/reserved.variables.server.php + 'PHP_SELF' => $non_empty_string_helper, + 'GATEWAY_INTERFACE' => $non_empty_string_helper, + 'SERVER_ADDR' => $non_empty_string_helper, + 'SERVER_NAME' => $non_empty_string_helper, + 'SERVER_SOFTWARE' => $non_empty_string_helper, + 'SERVER_PROTOCOL' => $non_empty_string_helper, + 'REQUEST_METHOD' => $non_empty_string_helper, + 'REQUEST_TIME' => $request_time_helper, + 'REQUEST_TIME_FLOAT' => $request_time_float_helper, + 'QUERY_STRING' => $string_helper, + 'DOCUMENT_ROOT' => $non_empty_string_helper, + 'HTTP_ACCEPT' => $non_empty_string_helper, + 'HTTP_ACCEPT_CHARSET' => $non_empty_string_helper, + 'HTTP_ACCEPT_ENCODING' => $non_empty_string_helper, + 'HTTP_ACCEPT_LANGUAGE' => $non_empty_string_helper, + 'HTTP_CONNECTION' => $non_empty_string_helper, + 'HTTP_HOST' => $non_empty_string_helper, + 'HTTP_REFERER' => $non_empty_string_helper, + 'HTTP_USER_AGENT' => $non_empty_string_helper, + 'HTTPS' => $string_helper, + 'REMOTE_ADDR' => $non_empty_string_helper, + 'REMOTE_HOST' => $non_empty_string_helper, + 'REMOTE_PORT' => $string_helper, + 'REMOTE_USER' => $non_empty_string_helper, + 'REDIRECT_REMOTE_USER' => $non_empty_string_helper, + 'SCRIPT_FILENAME' => $non_empty_string_helper, + 'SERVER_ADMIN' => $non_empty_string_helper, + 'SERVER_PORT' => $non_empty_string_helper, + 'SERVER_SIGNATURE' => $non_empty_string_helper, + 'PATH_TRANSLATED' => $non_empty_string_helper, + 'SCRIPT_NAME' => $non_empty_string_helper, + 'REQUEST_URI' => $non_empty_string_helper, + 'PHP_AUTH_DIGEST' => $non_empty_string_helper, + 'PHP_AUTH_USER' => $non_empty_string_helper, + 'PHP_AUTH_PW' => $non_empty_string_helper, + 'AUTH_TYPE' => $non_empty_string_helper, + 'PATH_INFO' => $non_empty_string_helper, + 'ORIG_PATH_INFO' => $non_empty_string_helper, + // misc from RFC not included above already http://www.faqs.org/rfcs/rfc3875.html + 'CONTENT_LENGTH' => $string_helper, + 'CONTENT_TYPE' => $string_helper, + // common, misc stuff + 'FCGI_ROLE' => $non_empty_string_helper, + 'HOME' => $non_empty_string_helper, + 'HTTP_CACHE_CONTROL' => $non_empty_string_helper, + 'HTTP_COOKIE' => $non_empty_string_helper, + 'HTTP_PRIORITY' => $non_empty_string_helper, + 'PATH' => $non_empty_string_helper, + 'REDIRECT_STATUS' => $non_empty_string_helper, + 'REQUEST_SCHEME' => $non_empty_string_helper, + 'USER' => $non_empty_string_helper, + // common, misc headers + 'HTTP_UPGRADE_INSECURE_REQUESTS' => $non_empty_string_helper, + 'HTTP_X_FORWARDED_PROTO' => $non_empty_string_helper, + 'HTTP_CLIENT_IP' => $non_empty_string_helper, + 'HTTP_X_REAL_IP' => $non_empty_string_helper, + 'HTTP_X_FORWARDED_FOR' => $non_empty_string_helper, + 'HTTP_CF_CONNECTING_IP' => $non_empty_string_helper, + 'HTTP_CF_IPCOUNTRY' => $non_empty_string_helper, + 'HTTP_CF_VISITOR' => $non_empty_string_helper, + 'HTTP_CDN_LOOP' => $non_empty_string_helper, + // common, misc browser headers + 'HTTP_DNT' => $non_empty_string_helper, + 'HTTP_SEC_FETCH_DEST' => $non_empty_string_helper, + 'HTTP_SEC_FETCH_USER' => $non_empty_string_helper, + 'HTTP_SEC_FETCH_MODE' => $non_empty_string_helper, + 'HTTP_SEC_FETCH_SITE' => $non_empty_string_helper, + 'HTTP_SEC_CH_UA_PLATFORM' => $non_empty_string_helper, + 'HTTP_SEC_CH_UA_MOBILE' => $non_empty_string_helper, + 'HTTP_SEC_CH_UA' => $non_empty_string_helper, + // phpunit + 'APP_DEBUG' => $bool_string_helper, + 'APP_ENV' => $string_helper, + ]; - $bool_string_helper = new Union([new TBool(), new TString()]); - $bool_string_helper->possibly_undefined = true; + if ($var_id === '$_SERVER') { + $arr['argv'] = $argv_helper; + $arr['argc'] = $argc_helper; + } $detailed_type = new TKeyedArray( - [ - // https://www.php.net/manual/en/reserved.variables.server.php - 'PHP_SELF' => $non_empty_string_helper, - 'argv' => $argv_helper, - 'argc' => $argc_helper, - 'GATEWAY_INTERFACE' => $non_empty_string_helper, - 'SERVER_ADDR' => $non_empty_string_helper, - 'SERVER_NAME' => $non_empty_string_helper, - 'SERVER_SOFTWARE' => $non_empty_string_helper, - 'SERVER_PROTOCOL' => $non_empty_string_helper, - 'REQUEST_METHOD' => $non_empty_string_helper, - 'REQUEST_TIME' => $request_time_helper, - 'REQUEST_TIME_FLOAT' => $request_time_float_helper, - 'QUERY_STRING' => $string_helper, - 'DOCUMENT_ROOT' => $non_empty_string_helper, - 'HTTP_ACCEPT' => $non_empty_string_helper, - 'HTTP_ACCEPT_CHARSET' => $non_empty_string_helper, - 'HTTP_ACCEPT_ENCODING' => $non_empty_string_helper, - 'HTTP_ACCEPT_LANGUAGE' => $non_empty_string_helper, - 'HTTP_CONNECTION' => $non_empty_string_helper, - 'HTTP_HOST' => $non_empty_string_helper, - 'HTTP_REFERER' => $non_empty_string_helper, - 'HTTP_USER_AGENT' => $non_empty_string_helper, - 'HTTPS' => $string_helper, - 'REMOTE_ADDR' => $non_empty_string_helper, - 'REMOTE_HOST' => $non_empty_string_helper, - 'REMOTE_PORT' => $string_helper, - 'REMOTE_USER' => $non_empty_string_helper, - 'REDIRECT_REMOTE_USER' => $non_empty_string_helper, - 'SCRIPT_FILENAME' => $non_empty_string_helper, - 'SERVER_ADMIN' => $non_empty_string_helper, - 'SERVER_PORT' => $non_empty_string_helper, - 'SERVER_SIGNATURE' => $non_empty_string_helper, - 'PATH_TRANSLATED' => $non_empty_string_helper, - 'SCRIPT_NAME' => $non_empty_string_helper, - 'REQUEST_URI' => $non_empty_string_helper, - 'PHP_AUTH_DIGEST' => $non_empty_string_helper, - 'PHP_AUTH_USER' => $non_empty_string_helper, - 'PHP_AUTH_PW' => $non_empty_string_helper, - 'AUTH_TYPE' => $non_empty_string_helper, - 'PATH_INFO' => $non_empty_string_helper, - 'ORIG_PATH_INFO' => $non_empty_string_helper, - // misc from RFC not included above already http://www.faqs.org/rfcs/rfc3875.html - 'CONTENT_LENGTH' => $string_helper, - 'CONTENT_TYPE' => $string_helper, - // common, misc stuff - 'FCGI_ROLE' => $non_empty_string_helper, - 'HOME' => $non_empty_string_helper, - 'HTTP_CACHE_CONTROL' => $non_empty_string_helper, - 'HTTP_COOKIE' => $non_empty_string_helper, - 'HTTP_PRIORITY' => $non_empty_string_helper, - 'PATH' => $non_empty_string_helper, - 'REDIRECT_STATUS' => $non_empty_string_helper, - 'REQUEST_SCHEME' => $non_empty_string_helper, - 'USER' => $non_empty_string_helper, - // common, misc headers - 'HTTP_UPGRADE_INSECURE_REQUESTS' => $non_empty_string_helper, - 'HTTP_X_FORWARDED_PROTO' => $non_empty_string_helper, - 'HTTP_CLIENT_IP' => $non_empty_string_helper, - 'HTTP_X_REAL_IP' => $non_empty_string_helper, - 'HTTP_X_FORWARDED_FOR' => $non_empty_string_helper, - 'HTTP_CF_CONNECTING_IP' => $non_empty_string_helper, - 'HTTP_CF_IPCOUNTRY' => $non_empty_string_helper, - 'HTTP_CF_VISITOR' => $non_empty_string_helper, - 'HTTP_CDN_LOOP' => $non_empty_string_helper, - // common, misc browser headers - 'HTTP_DNT' => $non_empty_string_helper, - 'HTTP_SEC_FETCH_DEST' => $non_empty_string_helper, - 'HTTP_SEC_FETCH_USER' => $non_empty_string_helper, - 'HTTP_SEC_FETCH_MODE' => $non_empty_string_helper, - 'HTTP_SEC_FETCH_SITE' => $non_empty_string_helper, - 'HTTP_SEC_CH_UA_PLATFORM' => $non_empty_string_helper, - 'HTTP_SEC_CH_UA_MOBILE' => $non_empty_string_helper, - 'HTTP_SEC_CH_UA' => $non_empty_string_helper, - // phpunit - 'APP_DEBUG' => $bool_string_helper, - 'APP_ENV' => $string_helper, - ], + $arr, null, false, Type::getNonEmptyString(), Type::getString() ); - + return new Union([$detailed_type]); } @@ -800,7 +796,7 @@ private static function getGlobalTypeInner(string $var_id, bool $files_full_path if ($files_full_path) { $values['full_path'] = $str; } - + $type = new Union([new TKeyedArray($values)]); $parent = new TArray([Type::getNonEmptyString(), $type]); @@ -808,15 +804,13 @@ private static function getGlobalTypeInner(string $var_id, bool $files_full_path } // $var_id === $_SESSION - + // keys must be string - $type = new Union([ + return new Union([ new TArray([ Type::getNonEmptyString(), Type::getMixed(), ]) - ]); - $type->possibly_undefined = true; - return $type; + ], ['possibly_undefined' => true]); } } diff --git a/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php b/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php index 3f6e50e81a7..efc9e3e7805 100644 --- a/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php +++ b/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php @@ -230,7 +230,7 @@ private function checkRemovableChainAssignment(PhpParser\Node\Expr $cur_assign, /** * @param array $stmts - * @return array{ + * @return strict-array{ * 0: PhpParser\Node\Stmt|null, * 1: PhpParser\Node\Expr\Assign|PhpParser\Node\Expr\AssignOp|PhpParser\Node\Expr\AssignRef|null * } @@ -320,7 +320,7 @@ private function findAssignStmt(array $stmts, string $var_id, CodeLocation $orig } /** - * @return array{ + * @return strict-array{ * 0: PhpParser\Node\Expr\Assign|PhpParser\Node\Expr\AssignOp|PhpParser\Node\Expr\AssignRef|null, * 1: int * } diff --git a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php index 8aba70acd41..13e5eb627fb 100644 --- a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php @@ -132,7 +132,7 @@ class StatementsAnalyzer extends SourceAnalyzer private $function_analyzers = []; /** - * @var array + * @var array */ private $unused_var_locations = []; @@ -981,7 +981,7 @@ public function registerVariableAssignment(string $var_id, CodeLocation $locatio } /** - * @return array + * @return array */ public function getUnusedVarLocations(): array { diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 5fa057c06e3..6d21bd9b16a 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -638,8 +638,8 @@ private static function initProviders(array $options, Config $config, string $cu } /** - * @param array{set-baseline:string} $options - * @return array}>> + * @param array{"set-baseline": string} $options + * @return array}>> */ private static function generateBaseline( array $options, @@ -679,7 +679,7 @@ private static function generateBaseline( } /** - * @return array}>> + * @return array}>> */ private static function updateBaseline(array $options, Config $config): array { @@ -978,7 +978,7 @@ private static function syncShortOptions(array &$options): void /** * @param array $args - * @return array{Config,?string} + * @return strict-array{Config,?string} */ private static function initConfig( string $current_dir, @@ -1015,7 +1015,7 @@ private static function initConfig( } /** - * @return array}>> + * @return array}>> */ private static function initBaseline( array $options, diff --git a/src/Psalm/Internal/Codebase/Analyzer.php b/src/Psalm/Internal/Codebase/Analyzer.php index 1239d28ed27..247d7ac8b43 100644 --- a/src/Psalm/Internal/Codebase/Analyzer.php +++ b/src/Psalm/Internal/Codebase/Analyzer.php @@ -5,8 +5,8 @@ use Closure; use InvalidArgumentException; use PhpParser; -use Psalm\CodeLocation; use Psalm\Codebase; +use Psalm\CodeLocation; use Psalm\Config; use Psalm\FileManipulation; use Psalm\Internal\Analyzer\FileAnalyzer; @@ -49,15 +49,15 @@ use const PHP_INT_MAX; /** - * @psalm-type TaggedCodeType = array + * @psalm-type TaggedCodeType = array * - * @psalm-type FileMapType = array{ + * @psalm-type FileMapType = strict-array{ * 0: TaggedCodeType, * 1: TaggedCodeType, - * 2: array + * 2: array * } * - * @psalm-type WorkerData = array{ + * @psalm-type WorkerData = strict-array{ * issues: array>, * fixable_issue_counts: array, * nonmethod_references_to_classes: array>, @@ -66,7 +66,7 @@ * file_references_to_class_properties: array>, * file_references_to_method_returns: array>, * file_references_to_missing_class_members: array>, - * mixed_counts: array, + * mixed_counts: array, * mixed_member_names: array>, * function_timings: array, * file_manipulations: array, @@ -120,7 +120,7 @@ class Analyzer /** * Used to store counts of mixed vs non-mixed variables * - * @var array + * @var array */ private $mixed_counts = []; @@ -176,17 +176,17 @@ class Analyzer private $existing_issues = []; /** - * @var array> + * @var array> */ private $reference_map = []; /** - * @var array> + * @var array> */ private $type_map = []; /** - * @var array> + * @var array> */ private $argument_map = []; @@ -911,8 +911,8 @@ public function loadCachedResults(ProjectAnalyzer $project_analyzer): void } /** - * @param array> $diff_map - * @param array> $deletion_ranges + * @param array> $diff_map + * @param array> $deletion_ranges */ public function shiftFileOffsets(array $diff_map, array $deletion_ranges): void { @@ -1103,7 +1103,7 @@ public function addMixedMemberNames(array $names): void } /** - * @return array{0:int, 1:int} + * @return strict-array{0:int, 1:int} */ public function getMixedCountsForFile(string $file_path): array { @@ -1115,7 +1115,7 @@ public function getMixedCountsForFile(string $file_path): array } /** - * @param array{0:int, 1:int} $mixed_counts + * @param strict-array{0:int, 1:int} $mixed_counts * */ public function setMixedCountsForFile(string $file_path, array $mixed_counts): void @@ -1167,7 +1167,7 @@ public function incrementNonMixedCount(string $file_path): void } /** - * @return array + * @return array */ public function getMixedCounts(): array { @@ -1256,7 +1256,7 @@ public function addOffsetReference(string $file_path, int $start, int $end, stri } /** - * @return array{int, int} + * @return strict-array{int, int} */ public function getTotalTypeCoverage(Codebase $codebase): array { diff --git a/src/Psalm/Internal/Codebase/ClassLikes.php b/src/Psalm/Internal/Codebase/ClassLikes.php index 664605ff5c4..8da40a3c71e 100644 --- a/src/Psalm/Internal/Codebase/ClassLikes.php +++ b/src/Psalm/Internal/Codebase/ClassLikes.php @@ -2252,7 +2252,7 @@ public function removeClassLike(string $fq_class_name): void } /** - * @return array{ + * @return strict-array{ * array, * array, * array, @@ -2280,7 +2280,7 @@ public function getThreadData(): array } /** - * @param array{ + * @param strict-array{ * 0: array, * 1: array, * 2: array, diff --git a/src/Psalm/Internal/Codebase/DataFlowGraph.php b/src/Psalm/Internal/Codebase/DataFlowGraph.php index 5f66a1955cc..e2bfc4e8c5e 100644 --- a/src/Psalm/Internal/Codebase/DataFlowGraph.php +++ b/src/Psalm/Internal/Codebase/DataFlowGraph.php @@ -109,7 +109,7 @@ protected static function shouldIgnoreFetch( } /** - * @return array{int, int, int, float} + * @return strict-array{int, int, int, float} */ public function getEdgeStats(): array { diff --git a/src/Psalm/Internal/Codebase/InternalCallMapHandler.php b/src/Psalm/Internal/Codebase/InternalCallMapHandler.php index 3222f9e680c..1c4083e6e1d 100644 --- a/src/Psalm/Internal/Codebase/InternalCallMapHandler.php +++ b/src/Psalm/Internal/Codebase/InternalCallMapHandler.php @@ -392,9 +392,9 @@ public static function getCallMap(): array continue; } /** - * @var array{ + * @var strict-array{ * added: array>, - * changed: array, * new: array * }>, diff --git a/src/Psalm/Internal/Codebase/Scanner.php b/src/Psalm/Internal/Codebase/Scanner.php index ab2d7742a8f..b8c74bdd668 100644 --- a/src/Psalm/Internal/Codebase/Scanner.php +++ b/src/Psalm/Internal/Codebase/Scanner.php @@ -4,6 +4,7 @@ use Closure; use Psalm\Codebase; +use Psalm\CodeLocation; use Psalm\Config; use Psalm\Internal\Analyzer\IssueData; use Psalm\Internal\Analyzer\ProjectAnalyzer; @@ -40,7 +41,7 @@ use const PHP_EOL; /** - * @psalm-type ThreadData = array{ + * @psalm-type ThreadData = strict-array{ * array, * array, * array, @@ -52,8 +53,8 @@ * array * } * - * @psalm-type PoolData = array{ - * classlikes_data:array{ + * @psalm-type PoolData = strict-array{ + * classlikes_data:strict-array{ * array, * array, * array, @@ -68,8 +69,8 @@ * issues:array>, * changed_members:array>, * unchanged_signature_members:array>, - * diff_map:array>, - * deletion_ranges:array>, + * diff_map:array>, + * deletion_ranges:array>, * errors:array, * classlike_storage:array, * file_storage:array, diff --git a/src/Psalm/Internal/Codebase/TaintFlowGraph.php b/src/Psalm/Internal/Codebase/TaintFlowGraph.php index bebf807215d..581538b8a05 100644 --- a/src/Psalm/Internal/Codebase/TaintFlowGraph.php +++ b/src/Psalm/Internal/Codebase/TaintFlowGraph.php @@ -172,7 +172,7 @@ public function getSuccessorPath(DataFlowNode $sink): string } /** - * @return list + * @return list */ public function getIssueTrace(DataFlowNode $source): array { diff --git a/src/Psalm/Internal/Diff/AstDiffer.php b/src/Psalm/Internal/Diff/AstDiffer.php index 7ab61cee590..1c9b6761be3 100644 --- a/src/Psalm/Internal/Diff/AstDiffer.php +++ b/src/Psalm/Internal/Diff/AstDiffer.php @@ -28,7 +28,7 @@ class AstDiffer * @param array $a * @param array $b * - * @return array{0:non-empty-list>, 1: int, 2: int, 3: array} + * @return strict-array{0:non-empty-list>, 1: int, 2: int, 3: array} */ protected static function calculateTrace( Closure $is_equal, diff --git a/src/Psalm/Internal/Diff/ClassStatementsDiffer.php b/src/Psalm/Internal/Diff/ClassStatementsDiffer.php index ccc6e947815..44f0c32eee3 100644 --- a/src/Psalm/Internal/Diff/ClassStatementsDiffer.php +++ b/src/Psalm/Internal/Diff/ClassStatementsDiffer.php @@ -22,12 +22,12 @@ class ClassStatementsDiffer extends AstDiffer * @param array $a * @param array $b * - * @return array{ + * @return strict-array{ * 0: list, * 1: list, * 2: list, - * 3: array, - * 4: list + * 3: array, + * 4: list * } */ public static function diff(string $name, array $a, array $b, string $a_code, string $b_code): array @@ -245,7 +245,7 @@ static function ( } } - /** @var array $diff_map */ + /** @var array $diff_map */ return [$keep, $keep_signature, $add_or_delete, $diff_map, $deletion_ranges]; } } diff --git a/src/Psalm/Internal/Diff/FileDiffer.php b/src/Psalm/Internal/Diff/FileDiffer.php index 675ce5c2c08..f35e1f8ffad 100644 --- a/src/Psalm/Internal/Diff/FileDiffer.php +++ b/src/Psalm/Internal/Diff/FileDiffer.php @@ -29,7 +29,7 @@ class FileDiffer * @param list $a * @param list $b * - * @return array{0:non-empty-list>, 1: int, 2: int} + * @return strict-array{0:non-empty-list>, 1: int, 2: int} * * @psalm-pure */ @@ -122,7 +122,7 @@ private static function extractDiff(array $trace, int $x, int $y, array $a, arra } /** - * @return array + * @return array * * @psalm-pure */ @@ -140,7 +140,7 @@ public static function getDiff(string $a_code, string $b_code): array $last_diff_type = null; - /** @var array{0:int, 1:int, 2:int, 3:int, 4:int, 5:string}|null */ + /** @var strict-array{0:int, 1:int, 2:int, 3:int, 4:int, 5:string}|null */ $last_change = null; $changes = []; diff --git a/src/Psalm/Internal/Diff/FileStatementsDiffer.php b/src/Psalm/Internal/Diff/FileStatementsDiffer.php index cb90a339c51..17f2bff0c89 100644 --- a/src/Psalm/Internal/Diff/FileStatementsDiffer.php +++ b/src/Psalm/Internal/Diff/FileStatementsDiffer.php @@ -18,12 +18,12 @@ class FileStatementsDiffer extends AstDiffer * @param list $a * @param list $b * - * @return array{ + * @return strict-array{ * 0: list, * 1: list, * 2: list, - * 3: list, - * 4: list + * 3: list, + * 4: list * } */ public static function diff(array $a, array $b, string $a_code, string $b_code): array diff --git a/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php b/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php index a3788f848e6..e423d5945bf 100644 --- a/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php +++ b/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php @@ -18,12 +18,12 @@ class NamespaceStatementsDiffer extends AstDiffer * @param array $a * @param array $b * - * @return array{ + * @return strict-array{ * 0: list, * 1: list, * 2: list, - * 3: list, - * 4: list + * 3: list, + * 4: list * } */ public static function diff(string $name, array $a, array $b, string $a_code, string $b_code): array diff --git a/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php b/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php index 25d731dcc96..c06cc1ddfe1 100644 --- a/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php +++ b/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php @@ -290,10 +290,10 @@ protected function fillGithubActions(): BuildInfoCollector if (isset($event_data['head_commit'])) { /** - * @var array{ + * @var strict-array{ * id: string, - * author: array{name: string, email: string}, - * committer: array{name: string, email: string}, + * author: strict-array{name: string, email: string}, + * committer: strict-array{name: string, email: string}, * message: string, * timestamp: string * } diff --git a/src/Psalm/Internal/FileManipulation/FileManipulationBuffer.php b/src/Psalm/Internal/FileManipulation/FileManipulationBuffer.php index 1ed9c6c180b..92bfbe1fe3f 100644 --- a/src/Psalm/Internal/FileManipulation/FileManipulationBuffer.php +++ b/src/Psalm/Internal/FileManipulation/FileManipulationBuffer.php @@ -49,7 +49,7 @@ public static function addCodeMigrations(array $code_migrations): void } /** - * @return array{int, int} + * @return strict-array{int, int} */ private static function getCodeOffsets( string $source_file_path, diff --git a/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php b/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php index 3541046cdca..5f9faf3f877 100644 --- a/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php +++ b/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php @@ -92,7 +92,7 @@ class FunctionDocblockManipulator /** @var array */ private $param_offsets = []; - /** @var array */ + /** @var array */ private $param_typehint_offsets = []; /** @var bool */ diff --git a/src/Psalm/Internal/LanguageServer/EmitterTrait.php b/src/Psalm/Internal/LanguageServer/EmitterTrait.php index 50535d0ed42..933455b1992 100644 --- a/src/Psalm/Internal/LanguageServer/EmitterTrait.php +++ b/src/Psalm/Internal/LanguageServer/EmitterTrait.php @@ -28,7 +28,7 @@ trait EmitterTrait /** * The list of listeners * - * @var array + * @var array */ protected $listeners = []; diff --git a/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php b/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php index 18c680054ef..f41d2e359d7 100644 --- a/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php @@ -32,7 +32,7 @@ public function __construct(int $offset, int $line_offset, array $extra_offsets) public function enterNode(PhpParser\Node $node): ?int { - /** @var array{startFilePos: int, endFilePos: int, startLine: int} */ + /** @var strict-array{startFilePos: int, endFilePos: int, startLine: int} */ $attrs = $node->getAttributes(); if ($cs = $node->getComments()) { diff --git a/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php b/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php index 08a9b0f6c2c..54cf269f0b9 100644 --- a/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php @@ -29,7 +29,7 @@ */ class PartialParserVisitor extends PhpParser\NodeVisitorAbstract { - /** @var array */ + /** @var array */ private $offset_map; /** @var bool */ @@ -53,7 +53,7 @@ class PartialParserVisitor extends PhpParser\NodeVisitorAbstract /** @var PhpParser\ErrorHandler\Collecting */ private $error_handler; - /** @param array $offset_map */ + /** @param array $offset_map */ public function __construct( PhpParser\Parser $parser, PhpParser\ErrorHandler\Collecting $error_handler, @@ -75,7 +75,7 @@ public function __construct( */ public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true) { - /** @var array{startFilePos: int, endFilePos: int, startLine: int} */ + /** @var strict-array{startFilePos: int, endFilePos: int, startLine: int} */ $attrs = $node->getAttributes(); if ($cs = $node->getComments()) { @@ -277,7 +277,7 @@ public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true) if ($error_handler->hasErrors()) { foreach ($error_handler->getErrors() as $error) { if ($error->hasColumnInfo()) { - /** @var array{startFilePos: int, endFilePos: int} */ + /** @var strict-array{startFilePos: int, endFilePos: int} */ $error_attrs = $error->getAttributes(); $error = new PhpParser\Error( $error->getRawMessage(), diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php index 0d71276b42e..bbfa1b56045 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php @@ -1291,7 +1291,6 @@ private function visitClassConstDeclaration( ); } } - $unresolved_node = null; if ($inferred_type && !( diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php index efd06cc0a7d..9bc6d5709b0 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php @@ -548,7 +548,7 @@ public static function parse( /** * @psalm-pure * @param list $line_parts - * @return array{string, string} $line_parts + * @return strict-array{string, string} $line_parts */ private static function sanitizeAssertionLineParts(array $line_parts): array { diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php index 5452d899e6b..91f1dd3ee55 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php @@ -425,8 +425,8 @@ public static function addDocblockInfo( * @param array|null $type_aliases * @param array> $function_template_types * - * @return array{ - * array, + * @return strict-array{ + * array, * array> * } */ @@ -675,7 +675,7 @@ private static function getAssertionParts( * @param array $type_aliases * @param array< * int, - * array{ + * strict-array{ * type:string, * name:string, * line_number:int, @@ -1362,7 +1362,7 @@ private static function handleAssertions( * @param array $type_aliases * @param array> $function_template_types * @param array> $class_template_types - * @param array{name:string, type:string, line_number: int} $docblock_param_out + * @param strict-array{name:string, type:string, line_number: int} $docblock_param_out */ private static function handleParamOut( array $docblock_param_out, diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index 487b60a8d9e..aefb2266e86 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -911,8 +911,9 @@ private function getTranslatedFunctionParam( ); } + //phpcs:disable -- Remove this once the phpstan phpdoc parser MR is merged /** - * @return array{ + * @return strict-array{ * string, * FunctionStorage|MethodStorage, * null|string, @@ -928,6 +929,7 @@ private function createStorageForFunctionLike( PhpParser\Node\FunctionLike $stmt, bool $fake_method ) { + //phpcs:enable -- Remove this once the phpstan phpdoc parser MR is merged $classlike_storage = null; $fq_classlike_name = null; $is_functionlike_override = false; diff --git a/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php b/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php index 0616249b1c0..e5cec7b434b 100644 --- a/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php +++ b/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php @@ -30,7 +30,7 @@ class SimpleNameResolver extends NodeVisitorAbstract /** * @param ErrorHandler $errorHandler Error handler - * @param null|array $offset_map + * @param null|array $offset_map */ public function __construct(ErrorHandler $errorHandler, ?array $offset_map = null) { @@ -84,7 +84,7 @@ public function enterNode(Node $node): ?int && $this->start_change && $this->end_change ) { - /** @var array{startFilePos: int, endFilePos: int} */ + /** @var strict-array{startFilePos: int, endFilePos: int} */ $attrs = $node->getAttributes(); if ($cs = $node->getComments()) { diff --git a/src/Psalm/Internal/PluginManager/Command/ShowCommand.php b/src/Psalm/Internal/PluginManager/Command/ShowCommand.php index 4e544c6c87e..8af8ff543cc 100644 --- a/src/Psalm/Internal/PluginManager/Command/ShowCommand.php +++ b/src/Psalm/Internal/PluginManager/Command/ShowCommand.php @@ -59,7 +59,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $formatRow = /** - * @return array{0: null|string, 1: string} + * @return strict-array{0: null|string, 1: string} */ static fn(string $class, ?string $package): array => [$package, $class]; diff --git a/src/Psalm/Internal/PluginManager/ComposerLock.php b/src/Psalm/Internal/PluginManager/ComposerLock.php index 7511d14c3c1..f5c890f9ab6 100644 --- a/src/Psalm/Internal/PluginManager/ComposerLock.php +++ b/src/Psalm/Internal/PluginManager/ComposerLock.php @@ -29,7 +29,10 @@ public function __construct(array $file_names) /** * @param mixed $package * - * @psalm-assert-if-true array{name: string, extra: array{psalm: array{pluginClass: string}}} $package + * @psalm-assert-if-true strict-array{ + * name: string, + * extra: strict-array{psalm: strict-array{pluginClass: string}} + * } $package * * @psalm-pure */ @@ -73,7 +76,7 @@ private function read(string $file_name): array } /** - * @return list + * @return list */ private function getAllPluginPackages(): array { diff --git a/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php b/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php index fdab4ac0e9a..90b24bf6c82 100644 --- a/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php +++ b/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php @@ -930,11 +930,13 @@ public function setFileMapCache(array $file_maps): void } } + //phpcs:disable -- Remove this once the phpstan phpdoc parser MR is merged /** - * @return array|false + * @return array|false */ public function getTypeCoverage() { + //phpcs:enable -- Remove this once the phpstan phpdoc parser MR is merged $cache_directory = Config::getInstance()->getCacheDirectory(); $type_coverage_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::TYPE_COVERAGE_CACHE_NAME; @@ -943,12 +945,12 @@ public function getTypeCoverage() && file_exists($type_coverage_cache_location) ) { if ($this->config->use_igbinary) { - /** @var array */ + /** @var array */ $type_coverage_cache = igbinary_unserialize( Providers::safeFileGetContents($type_coverage_cache_location) ); } else { - /** @var array */ + /** @var array */ $type_coverage_cache = unserialize( Providers::safeFileGetContents($type_coverage_cache_location) ); @@ -961,7 +963,7 @@ public function getTypeCoverage() } /** - * @param array $mixed_counts + * @param array $mixed_counts */ public function setTypeCoverage(array $mixed_counts): void { diff --git a/src/Psalm/Internal/Provider/FileReferenceProvider.php b/src/Psalm/Internal/Provider/FileReferenceProvider.php index 65afc1ff2c3..c93ffc63e0c 100644 --- a/src/Psalm/Internal/Provider/FileReferenceProvider.php +++ b/src/Psalm/Internal/Provider/FileReferenceProvider.php @@ -89,7 +89,7 @@ class FileReferenceProvider /** * A lookup table used for getting all the files referenced by a file * - * @var array, i:array}> + * @var array, i:array}> */ private static $file_references = []; @@ -159,7 +159,7 @@ class FileReferenceProvider private static $file_maps = []; /** - * @var array + * @var array */ private static $mixed_counts = []; @@ -1255,7 +1255,7 @@ public function setFileMaps(array $file_maps): void } /** - * @return array + * @return array */ public function getTypeCoverage(): array { @@ -1263,7 +1263,7 @@ public function getTypeCoverage(): array } /** - * @param array $mixed_counts + * @param array $mixed_counts * */ public function setTypeCoverage(array $mixed_counts): void diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php index 7b2112a93ae..2b6725d9723 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php @@ -118,7 +118,7 @@ static function ($keyed_type) use ($statements_source, $context) { return new Union([new TKeyedArray( $new_properties, null, - false, + $first_arg_array->sealed, null, null, $first_arg_array->is_list && $had_one diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php index 5c550b18792..b3796d38a7a 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php @@ -37,6 +37,8 @@ use function array_map; use function array_shift; use function array_slice; +use function array_values; +use function assert; use function count; use function explode; use function in_array; @@ -81,19 +83,33 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev foreach ($call_args as $call_arg) { $call_arg_type = $statements_source->node_data->getType($call_arg->value); - if ($call_arg_type) { - $array_arg_types[] = $call_arg_type; + if ($call_arg_type + && $call_arg_type->isSingle() + && ($call_arg_atomic = $call_arg_type->getSingleAtomic()) instanceof TKeyedArray + && $call_arg_atomic->sealed + ) { + $array_arg_types []= array_values($call_arg_atomic->properties); } else { - $array_arg_types[] = Type::getMixed(); - break; + return Type::getArray(); } } - if ($array_arg_types) { - return new Union([new TKeyedArray($array_arg_types)]); - } + $null = Type::getNull(); + $array_arg_types = array_map(null, ...$array_arg_types); + $array_arg_types = array_map( + /** @param non-empty-array $sub */ + function (array $sub) use ($null) { + $sub = array_map( + fn (?Union $t) => $t ?? $null, + $sub + ); + return new Union([new TKeyedArray($sub, null, true, null, null, true)]); + }, + $array_arg_types + ); + assert(count($array_arg_types)); - return Type::getArray(); + return new Union([new TKeyedArray($array_arg_types, null, true, null, null, true)]); } $array_arg = $call_args[1] ?? null; @@ -201,7 +217,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev null, $array_arg_atomic_type->sealed, $array_arg_atomic_type->previous_key_type, - $mapping_return_type, + $array_arg_atomic_type->sealed ? null : $mapping_return_type, $array_arg_atomic_type->is_list ); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index 91a9a7b5426..962325f873c 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -58,6 +58,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $generic_properties = []; $class_strings = []; $all_keyed_arrays = true; + $all_keyed_arrays_are_sealed = true; $all_int_offsets = true; $all_nonempty_lists = true; $any_nonempty = false; @@ -116,6 +117,8 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $generic_properties[] = $type; } continue; + } else { + $all_int_offsets = false; } if (isset($unpacked_type_part->class_strings[$key])) { @@ -143,6 +146,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $all_nonempty_lists = false; } + if (!$unpacked_type_part->sealed) { + $all_keyed_arrays_are_sealed = false; + } + if ($unpacked_type_part->sealed) { $any_nonempty = true; } @@ -240,7 +247,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $objectlike = new TKeyedArray( $generic_properties, $class_strings ?: null, - false, + $all_keyed_arrays && $all_keyed_arrays_are_sealed, $all_keyed_arrays ? null : $inner_key_type, $all_keyed_arrays ? null : $inner_value_type, $all_nonempty_lists || $all_int_offsets diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php index 992551318ef..20191869c9f 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php @@ -6,10 +6,13 @@ use Psalm\Context; use Psalm\Internal\Analyzer\ClassAnalyzer; use Psalm\Internal\Analyzer\SourceAnalyzer; +use Psalm\Internal\Analyzer\Statements\Expression\Fetch\AtomicPropertyFetchAnalyzer; use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent; use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface; use Psalm\Type; +use Psalm\Type\Atomic\TArray; +use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TObjectWithProperties; @@ -34,19 +37,23 @@ public static function getFunctionIds(): array ]; } + private static ?Union $fallback = null; + public static function getGetObjectVarsReturnType( Union $first_arg_type, SourceAnalyzer $statements_source, Context $context, CodeLocation $location ): Union { + self::$fallback ??= new Union([new TArray([Type::getString(), Type::getMixed()])]); + if ($first_arg_type->isSingle()) { $atomics = $first_arg_type->getAtomicTypes(); $object_type = reset($atomics); if ($object_type instanceof TObjectWithProperties) { if ([] === $object_type->properties) { - return Type::parseString('array'); + return self::$fallback; } return new Union([ new TKeyedArray($object_type->properties) @@ -55,13 +62,13 @@ public static function getGetObjectVarsReturnType( if ($object_type instanceof TNamedObject) { if (strtolower($object_type->value) === strtolower(stdClass::class)) { - return Type::parseString('array'); + return self::$fallback; } $codebase = $statements_source->getCodebase(); $class_storage = $codebase->classlikes->getStorageFor($object_type->value); if (null === $class_storage) { - return Type::parseString('array'); + return self::$fallback; } if ([] === $class_storage->appearing_property_ids) { @@ -69,7 +76,7 @@ public static function getGetObjectVarsReturnType( return Type::getEmptyArray(); } - return Type::parseString('array'); + return self::$fallback; } $properties = []; @@ -88,7 +95,21 @@ public static function getGetObjectVarsReturnType( $statements_source, $context ); - $properties[$name] = $property_type ?? Type::getMixed(); + if (!$property_type) { + continue; + } + + $property_type = $object_type instanceof TGenericObject + ? AtomicPropertyFetchAnalyzer::localizePropertyType( + $codebase, + $property_type, + $object_type, + $class_storage, + $class_storage + ) + : $property_type + ; + $properties[$name] = $property_type; } } @@ -97,15 +118,19 @@ public static function getGetObjectVarsReturnType( return Type::getEmptyArray(); } - return Type::parseString('array'); + return self::$fallback; } return new Union([ - new TKeyedArray($properties) + new TKeyedArray( + $properties, + null, + $class_storage->final + ) ]); } } - return Type::parseString('array'); + return self::$fallback; } public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php index 0abaa72c790..3cf991af2f8 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php @@ -146,7 +146,11 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $component_types['port'] = new Union([new TInt()], ['possibly_undefined' => true]); self::$return_type = new Union([ - new TKeyedArray($component_types), + new TKeyedArray( + $component_types, + null, + true + ), new TFalse(), ], [ 'ignore_falsable_issues' => $statements_source->getCodebase()->config->ignore_internal_falsable_issues diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index 6094a952726..5a02b1de7dd 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -85,12 +85,12 @@ class StatementsProvider private $errors = []; /** - * @var array> + * @var array> */ private $diff_map = []; /** - * @var array> + * @var array> */ private $deletion_ranges = []; @@ -367,7 +367,7 @@ public function setUnchangedFile(string $file_path): void } /** - * @return array> + * @return array> */ public function getDiffMap(): array { @@ -375,7 +375,7 @@ public function getDiffMap(): array } /** - * @return array> + * @return array> */ public function getDeletionRanges(): array { @@ -383,7 +383,7 @@ public function getDeletionRanges(): array } /** - * @param array> $diff_map + * @param array> $diff_map * */ public function addDiffMap(array $diff_map): void @@ -392,7 +392,7 @@ public function addDiffMap(array $diff_map): void } /** - * @param array> $deletion_ranges + * @param array> $deletion_ranges * */ public function addDeletionRanges(array $deletion_ranges): void @@ -411,7 +411,7 @@ public function resetDiffs(): void /** * @param list $existing_statements - * @param array $file_changes + * @param array $file_changes * * @return list */ diff --git a/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php b/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php index a9168cc39fd..21efdf96e36 100644 --- a/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php +++ b/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php @@ -43,7 +43,7 @@ class ClassLikeDocblockComment public $mixins = []; /** - * @var array + * @var array */ public $templates = []; @@ -63,7 +63,7 @@ class ClassLikeDocblockComment public $yield; /** - * @var array + * @var array */ public $properties = []; @@ -113,7 +113,7 @@ class ClassLikeDocblockComment public $suppressed_issues = []; /** - * @var list}> + * @var list}> */ public $imported_types = []; diff --git a/src/Psalm/Internal/Scanner/DocblockParser.php b/src/Psalm/Internal/Scanner/DocblockParser.php index 6015262e270..7a586e89cae 100644 --- a/src/Psalm/Internal/Scanner/DocblockParser.php +++ b/src/Psalm/Internal/Scanner/DocblockParser.php @@ -90,7 +90,7 @@ public static function parse(string $docblock, int $offsetStart): ParsedDocblock } if (preg_match('/^[ \t]*\*?\s*@([\w\-\\\:]+)[\t ]*(.*)$/sm', $line, $matches, PREG_OFFSET_CAPTURE)) { - /** @var array $matches */ + /** @var array $matches */ [, $type_info, $data_info] = $matches; [$type] = $type_info; diff --git a/src/Psalm/Internal/Scanner/FunctionDocblockComment.php b/src/Psalm/Internal/Scanner/FunctionDocblockComment.php index a3819b4acf7..5e9513e2471 100644 --- a/src/Psalm/Internal/Scanner/FunctionDocblockComment.php +++ b/src/Psalm/Internal/Scanner/FunctionDocblockComment.php @@ -35,7 +35,7 @@ class FunctionDocblockComment /** * @var array< * int, - * array{ + * strict-array{ * name:string, * type:string, * line_number: int, @@ -48,22 +48,22 @@ class FunctionDocblockComment public $params = []; /** - * @var array + * @var array */ public $params_out = []; /** - * @var array{type:string, line_number: int}|null + * @var strict-array{type:string, line_number: int}|null */ public $self_out; /** - * @var array{type:string, line_number: int}|null + * @var strict-array{type:string, line_number: int}|null */ public $if_this_is; /** - * @var array + * @var array */ public $globals = []; @@ -127,7 +127,7 @@ class FunctionDocblockComment public $removed_taints = []; /** - * @var array + * @var array */ public $taint_sink_params = []; @@ -137,7 +137,7 @@ class FunctionDocblockComment public $taint_source_types = []; /** - * @var array + * @var array */ public $assert_untainted_params = []; @@ -161,27 +161,27 @@ class FunctionDocblockComment public $suppressed_issues = []; /** - * @var array + * @var array */ public $throws = []; /** - * @var array + * @var array */ public $templates = []; /** - * @var array + * @var array */ public $assertions = []; /** - * @var array + * @var array */ public $if_true_assertions = []; /** - * @var array + * @var array */ public $if_false_assertions = []; @@ -223,6 +223,6 @@ class FunctionDocblockComment */ public $description; - /** @var array, suggested_replacement?:string}> */ + /** @var array, suggested_replacement?:string}> */ public $unexpected_tags = []; } diff --git a/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php b/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php index 7c372e438b8..9e30626e1b9 100644 --- a/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php +++ b/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php @@ -27,10 +27,21 @@ public static function isContainedBy( bool $allow_interface_equality, ?TypeComparisonResult $atomic_comparison_result ): bool { + $container_sealed = $container_type_part instanceof TKeyedArray + && $container_type_part->sealed; + + if ($container_sealed + && $input_type_part instanceof TKeyedArray + && !$input_type_part->sealed + ) { + return false; + } + $all_types_contain = true; + $input_properties = $input_type_part->properties; foreach ($container_type_part->properties as $key => $container_property_type) { - if (!isset($input_type_part->properties[$key])) { + if (!isset($input_properties[$key])) { if (!$container_property_type->possibly_undefined) { $all_types_contain = false; } @@ -38,7 +49,8 @@ public static function isContainedBy( continue; } - $input_property_type = $input_type_part->properties[$key]; + $input_property_type = $input_properties[$key]; + unset($input_properties[$key]); $property_type_comparison = new TypeComparisonResult(); @@ -82,6 +94,9 @@ public static function isContainedBy( } } } + if ($container_sealed && $input_properties) { + return false; + } return $all_types_contain; } diff --git a/src/Psalm/Internal/Type/ParseTreeCreator.php b/src/Psalm/Internal/Type/ParseTreeCreator.php index 0ee5d9b329f..b65fd109692 100644 --- a/src/Psalm/Internal/Type/ParseTreeCreator.php +++ b/src/Psalm/Internal/Type/ParseTreeCreator.php @@ -41,7 +41,7 @@ class ParseTreeCreator /** @var ParseTree */ private $current_leaf; - /** @var array */ + /** @var array */ private $type_tokens; /** @var int */ @@ -51,7 +51,7 @@ class ParseTreeCreator private $t = 0; /** - * @param list $type_tokens + * @param list $type_tokens */ public function __construct(array $type_tokens) { @@ -168,7 +168,7 @@ public function create(): ParseTree } /** - * @param array{0: string, 1: int} $current_token + * @param strict-array{0: string, 1: int, 2?: string} $current_token */ private function createMethodParam(array $current_token, ParseTree $current_parent): void { @@ -374,7 +374,7 @@ private function handleComma(): void $this->current_leaf = $context_node; } - /** @param array{0: string, 1: int} $type_token */ + /** @param strict-array{0: string, 1: int, 2?: string} $type_token */ private function handleEllipsisOrEquals(array $type_token): void { $prev_token = $this->t > 0 ? $this->type_tokens[$this->t - 1] : null; @@ -681,7 +681,7 @@ private function handleAmpersand(): void $this->current_leaf = $new_parent_leaf; } - /** @param array{0: string, 1: int} $type_token */ + /** @param strict-array{0: string, 1: int, 2?: string} $type_token */ private function handleIsOrAs(array $type_token): void { if ($this->t === 0) { @@ -724,7 +724,7 @@ private function handleIsOrAs(array $type_token): void } } - /** @param array{0: string, 1: int, 2?: string} $type_token */ + /** @param strict-array{0: string, 1: int, 2?: string} $type_token */ private function handleValue(array $type_token): void { $new_parent = !$this->current_leaf instanceof Root ? $this->current_leaf : null; diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index f5b26ce19d9..d441b08f730 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -75,6 +75,7 @@ use Psalm\Type\Reconciler; use Psalm\Type\Union; +use function array_map; use function array_merge; use function count; use function explode; @@ -576,14 +577,14 @@ private static function reconcileNonEmptyCountable( if ($existing_var_type->hasType('array')) { $array_atomic_type = $existing_var_type->getAtomicTypes()['array']; - $did_remove_type = false; + $redundant = true; if ($array_atomic_type instanceof TArray) { if (!$array_atomic_type instanceof TNonEmptyArray || ($assertion instanceof HasAtLeastCount && $array_atomic_type->min_count < $assertion->count) ) { - if ($array_atomic_type->getId() === 'array') { + if ($array_atomic_type->isEmptyArray()) { $existing_var_type->removeType('array'); } else { $non_empty_array = new TNonEmptyArray( @@ -595,7 +596,7 @@ private static function reconcileNonEmptyCountable( $existing_var_type->addType($non_empty_array); } - $did_remove_type = true; + $redundant = false; } } elseif ($array_atomic_type instanceof TList) { if (!$array_atomic_type instanceof TNonEmptyList @@ -608,32 +609,64 @@ private static function reconcileNonEmptyCountable( $assertion instanceof HasAtLeastCount ? $assertion->count : null ); - $did_remove_type = true; + $redundant = false; $existing_var_type->addType($non_empty_list); } } elseif ($array_atomic_type instanceof TKeyedArray) { - $prop_count = count($array_atomic_type->properties); - $min_count = 0; + $prop_max_count = count($array_atomic_type->properties); + $prop_min_count = 0; foreach ($array_atomic_type->properties as $property_type) { if (!$property_type->possibly_undefined) { - $min_count++; + $prop_min_count++; } } if ($assertion instanceof HasAtLeastCount) { - if ($array_atomic_type->sealed && $assertion->count > $min_count) { - $existing_var_type->removeType('array'); - $did_remove_type = true; - } elseif (!$array_atomic_type->sealed - && $array_atomic_type->is_list - && $min_count === $prop_count + if ($array_atomic_type->sealed) { + // count($a) > 3 + // count($a) >= 4 + + // 4 + $count = $assertion->count; + + // We're asserting that count($a) >= $count + // If it's impossible, remove the type + // If it's possible but redundant, mark as redundant + // If it's possible, mark as not redundant + + // Impossible because count($a) < $count always + if ($prop_max_count < $count) { + $redundant = false; + $existing_var_type->removeType('array'); + + // Redundant because count($a) >= $count always + } elseif ($prop_min_count >= $count) { + $redundant = true; + + // If count($a) === $count and there are possibly undefined properties + } elseif ($prop_max_count === $count && $prop_min_count !== $prop_max_count) { + $existing_var_type->removeType('array'); + $existing_var_type->addType($array_atomic_type->setProperties( + array_map( + fn (Union $union) => $union->setPossiblyUndefined(false), + $array_atomic_type->properties + ) + )); + $redundant = false; + + // Possible + } else { + $redundant = false; + } + } elseif ($array_atomic_type->is_list + && $prop_min_count === $prop_max_count ) { - if ($assertion->count <= $min_count) { - // this means a redundant condition + if ($assertion->count <= $prop_min_count) { + $redundant = true; } else { - $did_remove_type = true; + $redundant = false; $properties = $array_atomic_type->properties; - for ($i = $prop_count; $i < $assertion->count; $i++) { + for ($i = $prop_max_count; $i < $assertion->count; $i++) { $properties[$i] = ($array_atomic_type->previous_value_type ?: Type::getMixed()); } @@ -642,16 +675,16 @@ private static function reconcileNonEmptyCountable( $existing_var_type->addType($array_atomic_type); } } else { - $did_remove_type = true; + $redundant = false; } - } elseif ($min_count !== $prop_count) { - $did_remove_type = true; + } elseif ($prop_min_count !== $prop_max_count) { + $redundant = false; } } if (!$is_equality && !$existing_var_type->hasMixed() - && (!$did_remove_type || $existing_var_type->isUnionEmpty()) + && ($redundant || $existing_var_type->isUnionEmpty()) ) { if ($key && $code_location) { self::triggerIssueForImpossible( @@ -659,7 +692,7 @@ private static function reconcileNonEmptyCountable( $old_var_type_string, $key, $assertion, - !$did_remove_type, + $redundant, $negated, $code_location, $suppressed_issues @@ -700,6 +733,31 @@ private static function reconcileExactlyCountable( $existing_var_type->addType( $non_empty_list ); + } elseif ($array_atomic_type instanceof TKeyedArray) { + if ($array_atomic_type->sealed) { + if (count($array_atomic_type->properties) === $count) { + $existing_var_type->removeType('array'); + $existing_var_type->addType($array_atomic_type->setProperties( + array_map( + fn (Union $union) => $union->setPossiblyUndefined(false), + $array_atomic_type->properties + ) + )); + } + } else { + $has_possibly_undefined = false; + foreach ($array_atomic_type->properties as $property) { + if ($property->possibly_undefined) { + $has_possibly_undefined = true; + break; + } + } + + if (!$has_possibly_undefined && count($array_atomic_type->properties) === $count) { + $existing_var_type->removeType('array'); + $existing_var_type->addType($array_atomic_type->setSealed(true)); + } + } } } @@ -2046,7 +2104,7 @@ private static function reconcileList( } else { $array_types[] = $type; } - } elseif ($type instanceof TArray || $type instanceof TKeyedArray) { + } elseif ($type instanceof TArray || ($type instanceof TKeyedArray && !$type->sealed)) { if ($type instanceof TKeyedArray) { $type = $type->getGenericArrayType(); } diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index 1aa8772279a..ab1b5e927bc 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -62,6 +62,7 @@ use Psalm\Type\Union; use function assert; +use function count; use function get_class; use function max; use function strpos; @@ -512,7 +513,7 @@ private static function reconcileNotNonEmptyCountable( ?CodeLocation $code_location, array $suppressed_issues, bool $is_equality, - ?int $min_count + ?int $count ): Union { $existing_var_type = $existing_var_type->getBuilder(); $old_var_type_string = $existing_var_type->getId(); @@ -520,30 +521,63 @@ private static function reconcileNotNonEmptyCountable( if (isset($existing_var_atomic_types['array'])) { $array_atomic_type = $existing_var_atomic_types['array']; - $did_remove_type = false; + $redundant = true; if (($array_atomic_type instanceof TNonEmptyArray || $array_atomic_type instanceof TNonEmptyList) - && ($min_count === null - || $array_atomic_type->count >= $min_count - || $array_atomic_type->min_count >= $min_count) + && ($count === null + || $array_atomic_type->count >= $count + || $array_atomic_type->min_count >= $count) ) { - $did_remove_type = true; + $redundant = false; $existing_var_type->removeType('array'); } elseif ($array_atomic_type instanceof TKeyedArray) { - $did_remove_type = true; + if ($array_atomic_type->sealed && $count !== null) { + $prop_max_count = count($array_atomic_type->properties); + $prop_min_count = 0; + foreach ($array_atomic_type->properties as $property_type) { + if (!$property_type->possibly_undefined) { + $prop_min_count++; + } + } + + // !(count($a) >= 3) + // count($a) < 3 + + // We're asserting that count($a) < $count + // If it's impossible, remove the type + // If it's possible but redundant, mark as redundant + // If it's possible, mark as not redundant + + // Impossible because count($a) >= $count always + if ($prop_min_count >= $count) { + $redundant = false; + + $existing_var_type->removeType('array'); - foreach ($array_atomic_type->properties as $property_type) { - if (!$property_type->possibly_undefined) { - $did_remove_type = false; - break; + // Redundant because count($a) < $count always + } elseif ($prop_max_count < $count) { + $redundant = true; + + // Possible + } else { + $redundant = false; + } + } else { + $redundant = false; + + foreach ($array_atomic_type->properties as $property_type) { + if (!$property_type->possibly_undefined) { + $redundant = true; + break; + } } } } elseif (!$array_atomic_type instanceof TArray || !$array_atomic_type->isEmptyArray()) { - $did_remove_type = true; + $redundant = false; - if (!$min_count) { + if (!$count) { $existing_var_type->addType(new TArray( [ new Union([new TNever()]), @@ -555,7 +589,7 @@ private static function reconcileNotNonEmptyCountable( if (!$is_equality && !$existing_var_type->hasMixed() - && (!$did_remove_type || $existing_var_type->isUnionEmpty()) + && ($redundant || $existing_var_type->isUnionEmpty()) ) { if ($key && $code_location) { self::triggerIssueForImpossible( @@ -563,7 +597,7 @@ private static function reconcileNotNonEmptyCountable( $old_var_type_string, $key, $assertion, - !$did_remove_type, + $redundant, $negated, $code_location, $suppressed_issues diff --git a/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php b/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php index 44a8ac30ab8..bfc10f68891 100644 --- a/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php +++ b/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php @@ -15,12 +15,12 @@ class InlineTypeAlias implements TypeAlias use ImmutableNonCloneableTrait; /** - * @var list + * @var list */ public $replacement_tokens; /** - * @param list $replacement_tokens + * @param list $replacement_tokens */ public function __construct(array $replacement_tokens) { diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index 3c9076dae8f..ba7f741ef8b 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -131,7 +131,9 @@ public static function combine( if (count($combination->value_types) === 1 && !count($combination->objectlike_entries) - && !$combination->array_type_params + && (!$combination->array_type_params + || $combination->array_type_params[1]->isNever() + ) && !$combination->builtin_type_params && !$combination->object_type_params && !$combination->named_object_types @@ -1384,7 +1386,12 @@ private static function handleKeyedArrayEntries( $objectlike = new TCallableKeyedArray( $combination->objectlike_entries, null, - $combination->objectlike_sealed && !$combination->array_type_params, + $combination->objectlike_sealed && ( + !$combination->array_type_params + || (isset($combination->array_type_params[1]) + && $combination->array_type_params[1]->isNever() + ) + ), $previous_key_type, $previous_value_type, (bool)$combination->all_arrays_lists @@ -1393,7 +1400,12 @@ private static function handleKeyedArrayEntries( $objectlike = new TKeyedArray( $combination->objectlike_entries, null, - $combination->objectlike_sealed && !$combination->array_type_params, + $combination->objectlike_sealed && ( + !$combination->array_type_params + || (isset($combination->array_type_params[1]) + && $combination->array_type_params[1]->isNever() + ) + ), $previous_key_type, $previous_value_type, (bool)$combination->all_arrays_lists @@ -1419,7 +1431,7 @@ private static function handleKeyedArrayEntries( } /** - * @param array{Union, Union} $generic_type_params + * @param strict-array{Union, Union} $generic_type_params */ private static function getArrayTypeFromGenericParams( ?Codebase $codebase, @@ -1505,6 +1517,7 @@ private static function getArrayTypeFromGenericParams( if ($combination->all_arrays_lists) { if ($combination->objectlike_entries && $combination->objectlike_sealed + && isset($combination->array_type_params[1]) ) { $array_type = new TKeyedArray( [$generic_type_params[1]], diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index 17e7abb6528..a9700734f5a 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -327,46 +327,69 @@ public static function expandAtomic( $declaring_fq_classlike_name = $self_class; } - if ($evaluate_class_constants && $codebase->classOrInterfaceExists($declaring_fq_classlike_name)) { - $class_storage = $codebase->classlike_storage_provider->get($declaring_fq_classlike_name); - - $type_alias_name = $return_type->alias_name; - - if (isset($class_storage->type_aliases[$type_alias_name])) { - $resolved_type_alias = $class_storage->type_aliases[$type_alias_name]; - - if ($resolved_type_alias->replacement_atomic_types) { - $replacement_atomic_types = $resolved_type_alias->replacement_atomic_types; - - $recursively_fleshed_out_types = []; - - foreach ($replacement_atomic_types as $replacement_atomic_type) { - $more_recursively_fleshed_out_types = self::expandAtomic( - $codebase, - $replacement_atomic_type, - $self_class, - $static_class_type, - $parent_class, - $evaluate_class_constants, - $evaluate_conditional_types, - $final, - $expand_generic, - $expand_templates, - $throw_on_unresolvable_constant, - ); + if (!($evaluate_class_constants && $codebase->classOrInterfaceExists($declaring_fq_classlike_name))) { + return [$return_type]; + } - $recursively_fleshed_out_types = [ - ...$more_recursively_fleshed_out_types, - ...$recursively_fleshed_out_types - ]; - } + $class_storage = $codebase->classlike_storage_provider->get($declaring_fq_classlike_name); - return $recursively_fleshed_out_types; - } - } + $type_alias_name = $return_type->alias_name; + + if (!isset($class_storage->type_aliases[$type_alias_name])) { + return [$return_type]; } - return [$return_type]; + $resolved_type_alias = $class_storage->type_aliases[$type_alias_name]; + $replacement_atomic_types = $resolved_type_alias->replacement_atomic_types; + + if (!$replacement_atomic_types) { + return [$return_type]; + } + + $recursively_fleshed_out_types = []; + foreach ($replacement_atomic_types as $replacement_atomic_type) { + $more_recursively_fleshed_out_types = self::expandAtomic( + $codebase, + $replacement_atomic_type, + $self_class, + $static_class_type, + $parent_class, + $evaluate_class_constants, + $evaluate_conditional_types, + $final, + $expand_generic, + $expand_templates, + $throw_on_unresolvable_constant, + ); + + $recursively_fleshed_out_types = [ + ...$more_recursively_fleshed_out_types, + ...$recursively_fleshed_out_types + ]; + } + + foreach ($return_type->extra_types ?? [] as $alias) { + $more_recursively_fleshed_out_types = self::expandAtomic( + $codebase, + $alias, + $self_class, + $static_class_type, + $parent_class, + $evaluate_class_constants, + $evaluate_conditional_types, + $final, + $expand_generic, + $expand_templates, + $throw_on_unresolvable_constant, + ); + + $recursively_fleshed_out_types = [ + ...$more_recursively_fleshed_out_types, + ...$recursively_fleshed_out_types + ]; + } + + return $recursively_fleshed_out_types; } if ($return_type instanceof TKeyOf @@ -492,7 +515,7 @@ public static function expandAtomic( ); } unset($type_param); - /** @psalm-suppress ArgumentTypeCoercion Psalm bug */ + /** @psalm-suppress InvalidArgument Psalm bug */ $return_type = $return_type->setTypeParams($type_params); } elseif ($return_type instanceof TKeyedArray) { $properties = $return_type->properties; @@ -964,12 +987,16 @@ private static function expandPropertiesOf( return [$return_type]; } + $all_sealed = true; $properties = []; foreach ([$class_storage->name, ...array_values($class_storage->parent_classes)] as $class) { if (!$codebase->classExists($class)) { continue; } $storage = $codebase->classlike_storage_provider->get($class); + if (!$storage->final) { + $all_sealed = false; + } foreach ($storage->properties as $key => $property) { if (isset($properties[$key])) { continue; @@ -999,7 +1026,11 @@ private static function expandPropertiesOf( if ($properties === []) { return [$return_type]; } - return [new TKeyedArray($properties)]; + return [new TKeyedArray( + $properties, + null, + $all_sealed + )]; } /** diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index 8790c0f9ab7..fe0b35f99ce 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -89,6 +89,7 @@ use function preg_match; use function preg_replace; use function reset; +use function str_starts_with; use function stripslashes; use function strlen; use function strpos; @@ -104,8 +105,8 @@ class TypeParser /** * Parses a string type representation * - * @param list $type_tokens - * @param array{int,int}|null $php_version + * @param list $type_tokens + * @param strict-array{int,int}|null $php_version * @param array> $template_type_map * @param array $type_aliases * @@ -1124,8 +1125,13 @@ private static function getTypeFromIntersectionTree( array_pop($intersection_types); } + $all_sealed = true; + /** @var TKeyedArray $intersection_type */ foreach ($intersection_types as $intersection_type) { + if (!$intersection_type->sealed) { + $all_sealed = false; + } foreach ($intersection_type->properties as $property => $property_type) { if (!array_key_exists($property, $properties)) { $properties[$property] = $property_type; @@ -1161,7 +1167,7 @@ private static function getTypeFromIntersectionTree( return new TKeyedArray( $properties, null, - false, + $all_sealed, $previous_key_type ?? null, $previous_value_type ?? null, false, @@ -1380,7 +1386,7 @@ private static function getTypeFromKeyedArrayTree( $type = $parse_tree->value; - $is_tuple = true; + $is_list = true; foreach ($parse_tree->children as $i => $property_branch) { $class_string = false; @@ -1417,7 +1423,7 @@ private static function getTypeFromKeyedArrayTree( } else { $property_key = $property_branch->value; } - $is_tuple = false; + $is_list = false; } else { throw new TypeParseTreeException( 'Missing property type' @@ -1442,30 +1448,45 @@ private static function getTypeFromKeyedArrayTree( } } - if ($type !== 'array' && $type !== 'object' && $type !== 'callable-array') { - throw new TypeParseTreeException('Unexpected brace character'); - } - - if (!$properties) { - return new TArray([Type::getNever($from_docblock), Type::getNever($from_docblock)], $from_docblock); - } - if ($type === 'object') { return new TObjectWithProperties($properties, [], [], $from_docblock); } + $callable = str_starts_with($type, 'callable-'); $class = TKeyedArray::class; - if ($type === 'callable-array') { + if ($callable) { $class = TCallableKeyedArray::class; + $type = substr($type, 9); + } + + $sealed = str_starts_with($type, 'strict-'); + if ($sealed) { + $type = substr($type, 7); + } + + if ($callable && !$sealed) { + throw new TypeParseTreeException('A callable array cannot be unsealed!'); + } + + if ($callable && !$properties) { + throw new TypeParseTreeException('A callable array cannot be empty!'); + } + + if ($type !== 'array' && $type !== 'list') { + throw new TypeParseTreeException('Unexpected brace character'); + } + + if (!$properties) { + return new TArray([Type::getNever($from_docblock), Type::getNever($from_docblock)], $from_docblock); } return new $class( $properties, $class_strings, - $is_tuple, + $sealed, null, null, - $is_tuple, + $is_list, $from_docblock ); } diff --git a/src/Psalm/Internal/Type/TypeTokenizer.php b/src/Psalm/Internal/Type/TypeTokenizer.php index 842ca87bb68..d55f1ece6c4 100644 --- a/src/Psalm/Internal/Type/TypeTokenizer.php +++ b/src/Psalm/Internal/Type/TypeTokenizer.php @@ -38,6 +38,7 @@ class TypeTokenizer 'empty' => true, 'callable' => true, 'array' => true, + 'strict-array' => true, 'non-empty-array' => true, 'non-empty-string' => true, 'non-falsy-string' => true, @@ -84,6 +85,7 @@ class TypeTokenizer 'private-properties-of' => true, 'non-empty-countable' => true, 'list' => true, + 'strict-list' => true, 'non-empty-list' => true, 'class-string-map' => true, 'open-resource' => true, @@ -95,7 +97,7 @@ class TypeTokenizer ]; /** - * @var array> + * @var array> */ private static $memoized_tokens = []; @@ -103,7 +105,7 @@ class TypeTokenizer * Tokenises a type string into an array of tuples where the first element * contains the string token and the second element contains its offset, * - * @return list + * @return list * * @psalm-suppress PossiblyUndefinedIntArrayOffset */ @@ -299,7 +301,7 @@ public static function tokenize(string $string_type, bool $ignore_space = true): $was_space = false; } - /** @var list $type_tokens */ + /** @var list $type_tokens */ self::$memoized_tokens[$string_type] = $type_tokens; return $type_tokens; @@ -350,7 +352,7 @@ public static function fixScalarTerms( * @param array|null $template_type_map * @param array|null $type_aliases * - * @return list + * @return list */ public static function getFullyQualifiedTokens( string $string_type, @@ -503,7 +505,7 @@ public static function getFullyQualifiedTokens( } } - /** @var list */ + /** @var list */ return $type_tokens; } diff --git a/src/Psalm/Issue/TaintedInput.php b/src/Psalm/Issue/TaintedInput.php index 66f09b226e1..7eb7a4e1bbb 100644 --- a/src/Psalm/Issue/TaintedInput.php +++ b/src/Psalm/Issue/TaintedInput.php @@ -18,13 +18,13 @@ abstract class TaintedInput extends CodeIssue public $journey_text; /** - * @var list + * @var list * @readonly */ public $journey = []; /** - * @param list $journey + * @param list $journey */ public function __construct( string $message, @@ -39,7 +39,7 @@ public function __construct( } /** - * @return list + * @return list */ public function getTaintTrace(): array { diff --git a/src/Psalm/IssueBuffer.php b/src/Psalm/IssueBuffer.php index b2b80511031..bcb778202ba 100644 --- a/src/Psalm/IssueBuffer.php +++ b/src/Psalm/IssueBuffer.php @@ -533,7 +533,7 @@ public static function addIssues(array $issues_data): void } /** - * @param array}>> $issue_baseline + * @param array}>> $issue_baseline * */ public static function finish( @@ -833,7 +833,7 @@ public static function printSuccessMessage(ProjectAnalyzer $project_analyzer): v /** * @param array> $issues_data - * @param array{int, int} $mixed_counts + * @param strict-array{int, int} $mixed_counts * */ public static function getOutput( diff --git a/src/Psalm/Report/ConsoleReport.php b/src/Psalm/Report/ConsoleReport.php index f968326f1a0..6398b479240 100644 --- a/src/Psalm/Report/ConsoleReport.php +++ b/src/Psalm/Report/ConsoleReport.php @@ -77,7 +77,7 @@ private function format(IssueData $issue_data): string } /** - * @param non-empty-list $taint_trace + * @param non-empty-list $taint_trace */ private function getTaintSnippets(array $taint_trace): string { diff --git a/src/Psalm/Report/JunitReport.php b/src/Psalm/Report/JunitReport.php index 9ff30590819..d91185a87cf 100644 --- a/src/Psalm/Report/JunitReport.php +++ b/src/Psalm/Report/JunitReport.php @@ -101,7 +101,7 @@ public function create(): string } /** - * @param array{ + * @param strict-array{ * errors: int, * warnings: int, * failures: list diff --git a/src/Psalm/Report/PhpStormReport.php b/src/Psalm/Report/PhpStormReport.php index 75294cafd1e..e15f3afef03 100644 --- a/src/Psalm/Report/PhpStormReport.php +++ b/src/Psalm/Report/PhpStormReport.php @@ -61,7 +61,7 @@ private function format(IssueData $issue_data): string } /** - * @param non-empty-list $taint_trace + * @param non-empty-list $taint_trace */ private function getTaintSnippets(array $taint_trace): string { diff --git a/src/Psalm/Storage/FunctionLikeStorage.php b/src/Psalm/Storage/FunctionLikeStorage.php index 0ddabfabbb7..cba56b6fe44 100644 --- a/src/Psalm/Storage/FunctionLikeStorage.php +++ b/src/Psalm/Storage/FunctionLikeStorage.php @@ -225,7 +225,7 @@ abstract class FunctionLikeStorage implements HasAttributesInterface public $attributes = []; /** - * @var list, return: bool}>|null + * @var list, return: bool}>|null */ public $proxy_calls = []; diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index b00696b0b79..2d7e3b9eb9e 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -70,7 +70,7 @@ abstract class Type /** * Parses a string type representation * - * @param array{int,int}|null $php_version + * @param strict-array{int,int}|null $php_version * @param array> $template_type_map */ public static function parseString( diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 26f003c8979..ae2276cbb40 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -289,7 +289,7 @@ private static function createInner( case 'positive-int': return new TIntRange(1, null); - + case 'non-positive-int': return new TIntRange(null, 0); diff --git a/src/Psalm/Type/Atomic/CallableTrait.php b/src/Psalm/Type/Atomic/CallableTrait.php index f53d3734217..6368058b701 100644 --- a/src/Psalm/Type/Atomic/CallableTrait.php +++ b/src/Psalm/Type/Atomic/CallableTrait.php @@ -205,7 +205,7 @@ public function getId(bool $exact = true, bool $nested = false): string } /** - * @return array{list|null, Union|null}|null + * @return strict-array{list|null, Union|null}|null */ protected function replaceCallableTemplateTypesWithStandins( TemplateResult $template_result, @@ -281,7 +281,7 @@ protected function replaceCallableTemplateTypesWithStandins( /** - * @return array{list|null, Union|null}|null + * @return strict-array{list|null, Union|null}|null */ protected function replaceCallableTemplateTypesWithArgTypes( TemplateResult $template_result, diff --git a/src/Psalm/Type/Atomic/TArray.php b/src/Psalm/Type/Atomic/TArray.php index 7a15176dac7..fc8f030f109 100644 --- a/src/Psalm/Type/Atomic/TArray.php +++ b/src/Psalm/Type/Atomic/TArray.php @@ -18,12 +18,12 @@ class TArray extends Atomic { /** - * @use GenericTrait + * @use GenericTrait */ use GenericTrait; /** - * @var array{Union, Union} + * @var strict-array{Union, Union} */ public array $type_params; @@ -35,7 +35,7 @@ class TArray extends Atomic /** * Constructs a new instance of a generic type * - * @param array{Union, Union} $type_params + * @param strict-array{Union, Union} $type_params */ public function __construct(array $type_params, bool $from_docblock = false) { diff --git a/src/Psalm/Type/Atomic/TCallableKeyedArray.php b/src/Psalm/Type/Atomic/TCallableKeyedArray.php index 21a774e454e..4c452f8e942 100644 --- a/src/Psalm/Type/Atomic/TCallableKeyedArray.php +++ b/src/Psalm/Type/Atomic/TCallableKeyedArray.php @@ -8,10 +8,6 @@ */ final class TCallableKeyedArray extends TKeyedArray { - public const KEY = 'callable-array'; - - public function getKey(bool $include_extra = true): string - { - return 'array'; - } + protected const NAME_ARRAY = 'callable-array'; + protected const NAME_LIST = 'callable-list'; } diff --git a/src/Psalm/Type/Atomic/TIterable.php b/src/Psalm/Type/Atomic/TIterable.php index 8df955ff520..57446c62daa 100644 --- a/src/Psalm/Type/Atomic/TIterable.php +++ b/src/Psalm/Type/Atomic/TIterable.php @@ -21,12 +21,12 @@ final class TIterable extends Atomic { use HasIntersectionTrait; /** - * @use GenericTrait + * @use GenericTrait */ use GenericTrait; /** - * @var array{Union, Union} + * @var strict-array{Union, Union} */ public array $type_params; @@ -41,7 +41,7 @@ final class TIterable extends Atomic public $has_docblock_params = false; /** - * @param array{Union, Union}|array $type_params + * @param strict-array{Union, Union}|array $type_params * @param array $extra_types */ public function __construct(array $type_params = [], array $extra_types = [], bool $from_docblock = false) diff --git a/src/Psalm/Type/Atomic/TKeyedArray.php b/src/Psalm/Type/Atomic/TKeyedArray.php index be933b211dd..b0a6c382630 100644 --- a/src/Psalm/Type/Atomic/TKeyedArray.php +++ b/src/Psalm/Type/Atomic/TKeyedArray.php @@ -46,7 +46,10 @@ class TKeyedArray extends Atomic public $class_strings; /** - * @var bool - whether or not the objectlike has been created from an explicit array + * True if the objectlike has been created from an explicit array, + * or if we're sure that this objectlike has only these keys. + * + * @var bool */ public $sealed = false; @@ -70,7 +73,9 @@ class TKeyedArray extends Atomic public $is_list = false; /** @var non-empty-lowercase-string */ - public const KEY = 'array'; + protected const NAME_ARRAY = 'array'; + /** @var non-empty-lowercase-string */ + protected const NAME_LIST = 'list'; /** * Constructs a new instance of a generic type @@ -87,6 +92,9 @@ public function __construct( bool $is_list = false, bool $from_docblock = false ) { + if ($previous_key_type || $previous_value_type) { + $sealed = false; + } $this->properties = $properties; $this->class_strings = $class_strings; $this->sealed = $sealed; @@ -111,12 +119,41 @@ public function setProperties(array $properties): self return $cloned; } + /** + * @return static + */ + public function setSealed(bool $sealed): self + { + if ($sealed === $this->sealed) { + return $this; + } + $cloned = clone $this; + $cloned->sealed = $sealed; + if ($sealed) { + $cloned->previous_key_type = null; + $cloned->previous_value_type = null; + } + return $cloned; + } + public function getId(bool $exact = true, bool $nested = false): string { $property_strings = []; + if ($this->is_list) { + $use_list_syntax = true; + foreach ($this->properties as $property) { + if ($property->possibly_undefined) { + $use_list_syntax = false; + break; + } + } + } else { + $use_list_syntax = false; + } + foreach ($this->properties as $name => $type) { - if ($this->is_list && $this->sealed) { + if ($use_list_syntax) { $property_strings[$name] = $type->getId($exact); continue; } @@ -132,11 +169,17 @@ public function getId(bool $exact = true, bool $nested = false): string . ': ' . $type->getId($exact); } - if (!$this->is_list) { + if ($this->is_list) { + $key = static::NAME_LIST; + } else { + $key = static::NAME_ARRAY; sort($property_strings); } + if ($this->sealed) { + $key = "strict-$key"; + } - return static::KEY . '{' . + return $key . '{' . implode(', ', $property_strings) . '}' . ($this->previous_value_type @@ -168,7 +211,29 @@ public function toNamespacedString( $suffixed_properties = []; + if ($this->is_list) { + $use_list_syntax = true; + foreach ($this->properties as $property) { + if ($property->possibly_undefined) { + $use_list_syntax = false; + break; + } + } + } else { + $use_list_syntax = false; + } + foreach ($this->properties as $name => $type) { + if ($use_list_syntax) { + $suffixed_properties[$name] = $type->toNamespacedString( + $namespace, + $aliased_classes, + $this_class, + false + ); + continue; + } + $class_string_suffix = ''; if (isset($this->class_strings[$name])) { $class_string_suffix = '::class'; @@ -185,7 +250,9 @@ public function toNamespacedString( ); } - return static::KEY . '{' . implode(', ', $suffixed_properties) . '}'; + return ($this->sealed ? 'strict-' : '') . + ($this->is_list ? static::NAME_LIST : static::NAME_ARRAY) . '{' . + implode(', ', $suffixed_properties) . '}'; } /** @@ -197,7 +264,7 @@ public function toPhpString( ?string $this_class, int $analysis_php_version_id ): string { - return $this->getKey(); + return 'array'; } public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool @@ -303,8 +370,7 @@ public function isNonEmpty(): bool public function getKey(bool $include_extra = true): string { - /** @var string */ - return static::KEY; + return 'array'; } /** @@ -414,7 +480,7 @@ public function equals(Atomic $other_type, bool $ensure_source_equality): bool public function getAssertionString(): string { - return $this->getKey(); + return $this->is_list ? 'list' : 'array'; } public function getList(): TList diff --git a/src/Psalm/Type/Atomic/TNonEmptyArray.php b/src/Psalm/Type/Atomic/TNonEmptyArray.php index 2c2fba076c8..c1118ab82a7 100644 --- a/src/Psalm/Type/Atomic/TNonEmptyArray.php +++ b/src/Psalm/Type/Atomic/TNonEmptyArray.php @@ -27,7 +27,7 @@ class TNonEmptyArray extends TArray public $value = 'non-empty-array'; /** - * @param array{Union, Union} $type_params + * @param strict-array{Union, Union} $type_params * @param positive-int|null $count * @param positive-int|null $min_count */ diff --git a/src/Psalm/Type/MutableUnion.php b/src/Psalm/Type/MutableUnion.php index 407e9658cdc..2786405f5fe 100644 --- a/src/Psalm/Type/MutableUnion.php +++ b/src/Psalm/Type/MutableUnion.php @@ -470,6 +470,7 @@ public function getBuilder(): self */ public function freeze(): Union { + /** @psalm-suppress InvalidArgument It's actually filtered internally */ return new Union($this->getAtomicTypes(), get_object_vars($this)); } diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index 8f6fff0a887..73d38b8b39f 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -96,7 +96,7 @@ class Reconciler * @param array $referenced_var_ids * @param array> $template_type_map * - * @return array{array, array} + * @return strict-array{array, array} * * @psalm-suppress ComplexMethod */ diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index f20744e50d4..2a964a3d735 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -15,7 +15,7 @@ /** * @psalm-immutable - * @psalm-type TProperties=array{ + * @psalm-type TProperties=strict-array{ * from_docblock?: bool, * from_calculation?: bool, * from_property?: bool, @@ -336,6 +336,7 @@ public function setTypes(array $types): self */ public function getBuilder(): MutableUnion { + /** @psalm-suppress InvalidArgument It's actually filtered internally */ return new MutableUnion($this->getAtomicTypes(), get_object_vars($this)); } diff --git a/stubs/CoreGenericFunctions.phpstub b/stubs/CoreGenericFunctions.phpstub index c1a589f6c1e..e80a831f78c 100644 --- a/stubs/CoreGenericFunctions.phpstub +++ b/stubs/CoreGenericFunctions.phpstub @@ -512,7 +512,7 @@ function sscanf(string $string, string $format, &...$vars) {} * * @return ( * func_num_args() is 1 - * ? array{dirname: string, basename: string, extension?: string, filename: string} + * ? strict-array{dirname: string, basename: string, extension?: string, filename: string} * : string * ) */ @@ -679,7 +679,7 @@ function explode(string $separator, string $string, int $limit = -1) : array {} * * @param TFlags $flags * - * @return (TFlags is 0|2 ? non-empty-list|false : (TFlags is 1|3 ? list|false : list|false)) + * @return (TFlags is 0|2 ? non-empty-list|false : (TFlags is 1|3 ? list|false : list|false)) * * @psalm-ignore-falsable-return */ @@ -1006,15 +1006,15 @@ function preg_replace_callback($pattern, $callback, $subject, int $limit = -1, & * : (TFlags is 2 * ? list> * : (TFlags is 256|257 - * ? array> + * ? array> * : (TFlags is 258 - * ? list> + * ? list> * : (TFlags is 512|513 * ? array> * : (TFlags is 514 * ? list> * : (TFlags is 770 - * ? list> + * ? list> * : array * ) * ) @@ -1036,9 +1036,9 @@ function preg_match_all($pattern, $subject, &$matches = [], int $flags = 1, int * @param string $subject * @param mixed $matches * @param TFlags $flags - * @param-out (TFlags is 256 ? array : + * @param-out (TFlags is 256 ? array : * TFlags is 512 ? array : - * TFlags is 768 ? array : + * TFlags is 768 ? array : * array * ) $matches * @return 1|0|false @@ -1138,7 +1138,7 @@ function bcdiv(string $num1, string $num2, int $scale = 0): ?string {} function strval ($value): string {} /** - * @return ($string is non-empty-string ? non-empty-list : non-empty-list|array{null}) + * @return ($string is non-empty-string ? non-empty-list : non-empty-list|strict-array{null}) * @psalm-pure * * @psalm-flow ($string) -> return diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 91c0a41c69c..268bba8e4a9 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -155,7 +155,7 @@ interface Throwable /** * @psalm-mutation-free - * @return list',args?:array}> + * @return list',args?:array}> */ public function getTrace() : array; @@ -234,7 +234,7 @@ class Exception implements Throwable /** * @psalm-mutation-free - * @return list',args?:array}> + * @return list',args?:array}> */ public final function getTrace() : array {} @@ -311,7 +311,7 @@ class Error implements Throwable /** * @psalm-mutation-free - * @return list',args?:array}> + * @return list',args?:array}> */ public final function getTrace() : array {} diff --git a/stubs/phpparser.phpstub b/stubs/phpparser.phpstub index 834f8f62539..856cd2feccc 100644 --- a/stubs/phpparser.phpstub +++ b/stubs/phpparser.phpstub @@ -18,5 +18,5 @@ abstract class CallLike extends Expr { * @psalm-pure * @return list */ - public function getArgs(): array{} + public function getArgs(): array {} } diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index c485f07b43a..950fcd1eba9 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -146,9 +146,6 @@ function foo(array $arr = false) : void {}' $this->analyzeFile('somefile.php', new Context()); } - /** - * @return iterable,ignored_issues?:list,php_version?:string}> - */ public function providerValidCodeParse(): iterable { return [ @@ -403,7 +400,7 @@ function foo() : array { ], 'allowOptionalParamsToBeEmptyArray' => [ 'code' => ' [ @@ -424,7 +421,7 @@ function example(string $_x) : void {}', ], 'megaClosureAnnotationWithoutSpacing' => [ 'code' => '|null), b?:Closure():array, c?:Closure():array, d?:Closure():array, e?:Closure():(array{f:null|string, g:null|string, h:null|string, i:string, j:mixed, k:mixed, l:mixed, m:mixed, n:bool, o?:array{0:string}}|null), p?:Closure():(array{f:null|string, g:null|string, h:null|string, q:string, i:string, j:mixed, k:mixed, l:mixed, m:mixed, n:bool, o?:array{0:string}}|null), r?:Closure():(array|null), s:array} */ + /** @var strict-array{a:Closure():(array|null), b?:Closure():array, c?:Closure():array, d?:Closure():array, e?:Closure():(strict-array{f:null|string, g:null|string, h:null|string, i:string, j:mixed, k:mixed, l:mixed, m:mixed, n:bool, o?:strict-array{0:string}}|null), p?:Closure():(strict-array{f:null|string, g:null|string, h:null|string, q:string, i:string, j:mixed, k:mixed, l:mixed, m:mixed, n:bool, o?:strict-array{0:string}}|null), r?:Closure():(array|null), s:array} */ $arr = []; $arr["a"]();', @@ -432,12 +429,12 @@ function example(string $_x) : void {}', 'megaClosureAnnotationWithSpacing' => [ 'code' => '|null), * b?: Closure() : array, * c?: Closure() : array, * d?: Closure() : array, - * e?: Closure() : (array{ + * e?: Closure() : (strict-array{ * f: null|string, * g: null|string, * h: null|string, @@ -447,9 +444,9 @@ function example(string $_x) : void {}', * l: mixed, * m: mixed, * n: bool, - * o?: array{0:string} + * o?: strict-array{0:string} * }|null), - * p?: Closure() : (array{ + * p?: Closure() : (strict-array{ * f: null|string, * g: null|string, * h: null|string, @@ -460,7 +457,7 @@ function example(string $_x) : void {}', * l: mixed, * m: mixed, * n: bool, - * o?: array{0:string} + * o?: strict-array{0:string} * }|null), * r?: Closure() : (array|null), * s: array @@ -480,7 +477,7 @@ function example(string $_x) : void {}', * array * > * - * @psalm-type RuleArray = array{ + * @psalm-type RuleArray = strict-array{ * rule: string, * controller?: class-string<\Exception>, * redirect?: string, @@ -496,7 +493,7 @@ class A {}', 'builtInClassInAShape' => [ 'code' => ' [ 'code' => ' [ 'code' => ', 1:list} */ + /** @var strict-array{0:list, 1:list} */ $bar = [[], []]; foo($bar);' @@ -1032,7 +1029,7 @@ public static function create(): void { 'parseTrailingCommaInReturn' => [ 'code' => ' ' "literal"];', 'assertions' => [ - '$_arr' => 'array{\'foo\\\\bar\nbaz\': string}' + '$_arr' => 'strict-array{\'foo\\\\bar\nbaz\': string}' ] ], 'doubleSpaceBeforeAt' => [ @@ -1101,7 +1098,7 @@ function bar() : void {}' function example(string $s): void { if (preg_match(\'{foo-(\w+)}\', $s, $m)) { - /** @var array{string, string} $m */ + /** @var strict-array{string, string} $m */ takesString($m[1]); } } @@ -1255,7 +1252,7 @@ public function __construct( } /** - * @return iterable}> + * */ public function providerInvalidCodeParse(): iterable { @@ -1561,7 +1558,7 @@ class A {}', * @psalm-type aType null|"a"|"b"|"c"|"d" */ - /** @psalm-return array{0:bool,1:aType} */ + /** @psalm-return strict-array{0:bool,1:aType} */ function f(): array { return [(bool)rand(0,1), rand(0,1) ? "z" : null]; }', @@ -1578,7 +1575,7 @@ class A { 'noCrashOnHalfDoneTKeyedArrayPropertyType' => [ 'code' => ' 'InvalidDocblock', @@ -1629,7 +1626,7 @@ class A {} 'badPsalmType' => [ 'code' => ' 'InvalidDocblock', ], @@ -1729,7 +1726,7 @@ function foo(&...$s) : void {} ], 'identifyReturnType' => [ 'code' => ' 'InvalidReturnType - src' . DIRECTORY_SEPARATOR . 'somefile.php:2:33', ], diff --git a/tests/ArgTest.php b/tests/ArgTest.php index f643fc1d402..90bb1ebb3fd 100644 --- a/tests/ArgTest.php +++ b/tests/ArgTest.php @@ -11,7 +11,7 @@ class ArgTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -39,7 +39,7 @@ function add(int $a, int $b, int $c) : int { $a = [[1, 2]]; $b = array_merge([], ...$a);', 'assertions' => [ - '$b' => 'array{0: int, 1: int}', + '$b===' => 'strict-list{1, 2}', ], ], 'preserveTypesWhenUnpacking' => [ @@ -120,7 +120,7 @@ class Hello {} sort($c); ', 'assertions' => [ - '$a' => 'array{a: int, b: int}', + '$a' => 'strict-array{a: int, b: int}', '$b' => 'non-empty-list', '$c' => 'list', ], @@ -241,7 +241,7 @@ public function __construct( } /** - * @param array{age: int, name: string, email: string} $input + * @param strict-array{age: int, name: string, email: string} $input */ function foo(array $input) : CustomerData { return new CustomerData( @@ -312,11 +312,22 @@ public function foo(int ...$values): array } ', ], + 'SealedAcceptSealed' => [ + 'code' => ' "str"]; + a($sealed); + ', + ], ]; } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -399,7 +410,7 @@ public function __construct( } /** - * @param array{age: int, name: string, email: string} $input + * @param strict-array{age: int, name: string, email: string} $input */ function foo(array $input) : CustomerData { return new CustomerData( @@ -434,7 +445,7 @@ public function __construct( } /** - * @param array{aage: int, name: string, email: string} $input + * @param strict-array{aage: int, name: string, email: string} $input */ function foo(array $input) : CustomerData { return new CustomerData(...$input); @@ -456,7 +467,7 @@ public function __construct( } /** - * @param array{age: int, name: string, email: string} $input + * @param strict-array{age: int, name: string, email: string} $input */ function foo(array $input) : CustomerData { return new CustomerData( @@ -486,7 +497,7 @@ public function __construct( } /** - * @param array{id: int, name: string} $data + * @param strict-array{id: int, name: string} $data */ function processUserDataInvalid(array $data) : User { return new User(...$data); @@ -506,7 +517,7 @@ public function __construct( } /** - * @param array{id: int, name: string} $data + * @param strict-array{id: int, name: string} $data */ function processUserDataInvalid(array $data) : User { /** @psalm-suppress MixedArgument */ @@ -724,6 +735,31 @@ public function __construct( ', 'error_message' => 'TooFewArguments', ], + 'SealedRefuseUnsealed' => [ + 'code' => ' 'InvalidArgument', + ], + 'SealedRefuseSealedExtra' => [ + 'code' => ' "str", "somethingElse" => "test"]; + a($sealedExtraKeys); + ', + 'error_message' => 'InvalidArgument', + ], ]; } } diff --git a/tests/ArrayAccessTest.php b/tests/ArrayAccessTest.php index fdf1f43914d..23d25cae16f 100644 --- a/tests/ArrayAccessTest.php +++ b/tests/ArrayAccessTest.php @@ -431,7 +431,7 @@ function foo(array $a, int $b): void { } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -528,7 +528,7 @@ function takesString(string $s): void {} function takesBool(bool $b): void {} /** - * @param array{int, string, bool} $b + * @param strict-array{int, string, bool} $b */ function a(array $b): void { takesInt($b[0]); @@ -555,7 +555,7 @@ function a(array $b): void { namespace N; /** - * @psalm-param array{key?:string} $p + * @psalm-param strict-array{key?:string} $p */ function f(array $p): void { @@ -575,8 +575,8 @@ function f(array $p): void unset($x3[$k]);', 'assertions' => [ '$x1===' => 'array', - '$x2===' => "array{b: 'value'}", - '$x3===' => "array{b: 'value'}", + '$x2===' => "strict-array{b: 'value'}", + '$x3===' => "strict-array{b: 'value'}", ] ], 'domNodeListAccessible' => [ @@ -605,7 +605,7 @@ function example(array $x, $y) : void { ], 'suppressPossiblyUndefinedStringArrayOffet' => [ 'code' => ' $elt] = $entry; @@ -886,7 +886,7 @@ public function offsetUnset($offset) : void 'arrayAccessAfterByRefArrayOffsetAssignment' => [ 'code' => ' [ 'code' => ' [ '$_arr1===' => 'non-empty-array<1, 5>', - '$_arr2===' => 'array{1: 5}', + '$_arr2===' => 'strict-array{1: 5}', ] ], 'accessArrayWithSingleStringLiteralOffset' => [ @@ -1122,13 +1122,13 @@ function f($p): int { $a = ["a", "b"]; unset($a[0]); ', - 'assertions' => ['$a===' => "array{1: 'b'}"] + 'assertions' => ['$a===' => "strict-array{1: 'b'}"] ], ]; } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -1181,7 +1181,7 @@ function return_array() { $a = []; echo $a[0];', 'error_message' => 'MixedArrayAccess', - 'error_level' => ['MixedAssignment'], + 'ignored_issues' => ['MixedAssignment'], ], 'mixedArrayOffset' => [ 'code' => ' 'MixedArrayOffset', - 'error_level' => ['MixedAssignment'], + 'ignored_issues' => ['MixedAssignment'], ], 'nullArrayAccess' => [ 'code' => ' [ 'code' => ' [ 'code' => ' $elt] = $entry;', @@ -1370,7 +1370,7 @@ function foo(SimpleXMLElement $s) : void { 'code' => ' [ 'code' => ',ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -65,7 +65,7 @@ public function providerValidCodeParse(): iterable $out[] = [4]; }', 'assertions' => [ - '$out' => 'non-empty-list', + '$out' => 'non-empty-list', ], ], 'generic2dArrayCreationAddedInIf' => [ @@ -193,8 +193,8 @@ class B {} $bat[$text] = $bar[$i]; }', 'assertions' => [ - '$foo' => 'array{0: string, 1: string, 2: string}', - '$bar' => 'array{int, int, int}', + '$foo' => 'strict-array{0: string, 1: string, 2: string}', + '$bar' => 'strict-list{int, int, int}', '$bat' => 'non-empty-array', ], ], @@ -203,7 +203,7 @@ class B {} $foo = []; $foo["bar"] = "hello";', 'assertions' => [ - '$foo' => 'array{bar: string}', + '$foo' => 'strict-array{bar: string}', '$foo[\'bar\']' => 'string', ], ], @@ -212,7 +212,7 @@ class B {} $foo = []; $foo["bar"]["baz"] = "hello";', 'assertions' => [ - '$foo' => 'array{bar: array{baz: string}}', + '$foo' => 'strict-array{bar: strict-array{baz: string}}', '$foo[\'bar\'][\'baz\']' => 'string', ], ], @@ -221,7 +221,7 @@ class B {} $foo = []; $foo["bar"]["baz"]["bat"] = "hello";', 'assertions' => [ - '$foo' => 'array{bar: array{baz: array{bat: string}}}', + '$foo' => 'strict-array{bar: strict-array{baz: strict-array{bat: string}}}', '$foo[\'bar\'][\'baz\'][\'bat\']' => 'string', ], ], @@ -230,7 +230,7 @@ class B {} $foo = []; $foo["bar"]["baz"]["bat"]["bap"] = "hello";', 'assertions' => [ - '$foo' => 'array{bar: array{baz: array{bat: array{bap: string}}}}', + '$foo' => 'strict-array{bar: strict-array{baz: strict-array{bat: strict-array{bap: string}}}}', '$foo[\'bar\'][\'baz\'][\'bat\'][\'bap\']' => 'string', ], ], @@ -239,7 +239,7 @@ class B {} $foo = ["bar" => []]; $foo["bar"]["baz"] = "hello";', 'assertions' => [ - '$foo' => 'array{bar: array{baz: string}}', + '$foo' => 'strict-array{bar: strict-array{baz: string}}', '$foo[\'bar\'][\'baz\']' => 'string', ], ], @@ -248,7 +248,7 @@ class B {} $foo = ["bar" => []]; $foo["bar"]["baz"]["bat"] = "hello";', 'assertions' => [ - '$foo' => 'array{bar: array{baz: array{bat: string}}}', + '$foo' => 'strict-array{bar: strict-array{baz: strict-array{bat: string}}}', ], ], 'conflictingTypesWithNoAssignment' => [ @@ -258,7 +258,7 @@ class B {} "baz" => [1] ];', 'assertions' => [ - '$foo' => 'array{bar: array{a: string}, baz: array{int}}', + '$foo' => 'strict-array{bar: strict-array{a: string}, baz: strict-list{int}}', ], ], 'implicitTKeyedArrayCreation' => [ @@ -268,7 +268,7 @@ class B {} ]; $foo["baz"] = "a";', 'assertions' => [ - '$foo' => 'array{bar: int, baz: string}', + '$foo' => 'strict-array{bar: int, baz: string}', ], ], 'conflictingTypesWithAssignment' => [ @@ -279,7 +279,7 @@ class B {} ]; $foo["bar"]["bam"]["baz"] = "hello";', 'assertions' => [ - '$foo' => 'array{bar: array{a: string, bam: array{baz: string}}, baz: array{int}}', + '$foo' => 'strict-array{bar: strict-array{a: string, bam: strict-array{baz: string}}, baz: strict-list{int}}', ], ], 'conflictingTypesWithAssignment2' => [ @@ -289,7 +289,7 @@ class B {} $foo["b"][] = "goodbye"; $bar = $foo["a"];', 'assertions' => [ - '$foo' => 'array{a: string, b: non-empty-list}', + '$foo' => 'strict-array{a: string, b: non-empty-list}', '$foo[\'a\']' => 'string', '$foo[\'b\']' => 'non-empty-list', '$bar' => 'string', @@ -301,7 +301,7 @@ class B {} $foo["a"] = "hello"; $foo["b"]["c"]["d"] = "goodbye";', 'assertions' => [ - '$foo' => 'array{a: string, b: array{c: array{d: string}}}', + '$foo' => 'strict-array{a: string, b: strict-array{c: strict-array{d: string}}}', ], ], 'nestedTKeyedArrayAssignment' => [ @@ -310,7 +310,7 @@ class B {} $foo["a"]["b"] = "hello"; $foo["a"]["c"] = 1;', 'assertions' => [ - '$foo' => 'array{a: array{b: string, c: int}}', + '$foo' => 'strict-array{a: strict-array{b: string, c: int}}', ], ], 'conditionalTKeyedArrayAssignment' => [ @@ -323,7 +323,7 @@ class B {} $foo["b"] = 2; }', 'assertions' => [ - '$foo' => 'array{a: string, b: int}', + '$foo' => 'strict-array{a: string, b: int}', ], ], 'arrayKey' => [ @@ -342,7 +342,7 @@ class B {} 'conditionalCheck' => [ 'code' => ' [ - '$a' => 'array{boop: non-empty-list}', - '$c' => 'array{boop: array{boop: non-empty-list}}', + '$a' => 'strict-array{boop: non-empty-list}', + '$c' => 'strict-array{boop: strict-array{boop: non-empty-list}}', ], ], 'assignExplicitValueToGeneric' => [ @@ -371,7 +371,7 @@ function fooFoo($a) { $a = []; $a["foo"] = ["bar" => "baz"];', 'assertions' => [ - '$a' => 'array{foo: array{bar: string}}>', + '$a' => 'array{foo: strict-array{bar: string}}>', ], ], 'additionWithEmpty' => [ @@ -381,8 +381,8 @@ function fooFoo($a) { $b = [] + ["bar"];', 'assertions' => [ - '$a' => 'array{0: string}', - '$b' => 'array{0: string}', + '$a' => 'strict-list{string}', + '$b' => 'strict-list{string}', ], ], 'additionDifferentType' => [ @@ -392,8 +392,8 @@ function fooFoo($a) { $b = ["bar"] + [1];', 'assertions' => [ - '$a' => 'array{0: string}', - '$b' => 'array{0: string}', + '$a' => 'strict-array{0: string}', + '$b' => 'strict-array{0: string}', ], ], 'present1dArrayTypeWithVarKeys' => [ @@ -419,7 +419,7 @@ function fooFoo($a) { ], 'objectLikeWithIntegerKeys' => [ 'code' => ' [2, 3]];', 'assertions' => [ - '$foo' => 'array{a: int, b: array{int, int}}', + '$foo' => 'strict-array{a: int, b: strict-list{int, int}}', ], ], 'objectLikeArrayIsNonEmpty' => [ 'code' => ' */ function test(array $arg): array { @@ -457,7 +457,7 @@ function test(array $arg): array { $foo["root"]["a"] = 1; $foo["root"] += ["b" => [2, 3]];', 'assertions' => [ - '$foo' => 'array{root: array{a: int, b: array{int, int}}}', + '$foo' => 'strict-array{root: strict-array{a: int, b: strict-list{int, int}}}', ], ], 'updateStringIntKey1' => [ @@ -467,7 +467,7 @@ function test(array $arg): array { $a["a"] = 5; $a[0] = 3;', 'assertions' => [ - '$a' => 'array{0: int, a: int}', + '$a' => 'strict-array{0: int, a: int}', ], ], 'updateStringIntKey2' => [ @@ -479,7 +479,7 @@ function test(array $arg): array { $b[$string] = 5; $b[0] = 3;', 'assertions' => [ - '$b' => 'array{0: int, c: int}', + '$b' => 'strict-array{0: int, c: int}', ], ], 'updateStringIntKey3' => [ @@ -491,7 +491,7 @@ function test(array $arg): array { $c[0] = 3; $c[$string] = 5;', 'assertions' => [ - '$c' => 'array{0: int, c: int}', + '$c' => 'strict-array{0: int, c: int}', ], ], 'updateStringIntKey4' => [ @@ -503,7 +503,7 @@ function test(array $arg): array { $d[$int] = 3; $d["a"] = 5;', 'assertions' => [ - '$d' => 'array{5: int, a: int}', + '$d' => 'strict-array{5: int, a: int}', ], ], 'updateStringIntKey5' => [ @@ -516,7 +516,7 @@ function test(array $arg): array { $e[$int] = 3; $e[$string] = 5;', 'assertions' => [ - '$e' => 'array{5: int, c: int}', + '$e' => 'strict-array{5: int, c: int}', ], ], 'updateStringIntKeyWithIntRootAndNumberOffset' => [ @@ -529,7 +529,7 @@ function test(array $arg): array { $a[0]["a"] = 5; $a[0][0] = 3;', 'assertions' => [ - '$a' => 'array{0: array{0: int, a: int}}', + '$a' => 'strict-array{0: strict-array{0: int, a: int}}', ], ], 'updateStringIntKeyWithIntRoot' => [ @@ -557,10 +557,10 @@ function test(array $arg): array { $e[0][$int] = 3; $e[0][$string] = 5;', 'assertions' => [ - '$b' => 'array{0: array{0: int, c: int}}', - '$c' => 'array{0: array{0: int, c: int}}', - '$d' => 'array{0: array{5: int, a: int}}', - '$e' => 'array{0: array{5: int, c: int}}', + '$b' => 'strict-array{0: strict-array{0: int, c: int}}', + '$c' => 'strict-array{0: strict-array{0: int, c: int}}', + '$d' => 'strict-array{0: strict-array{5: int, a: int}}', + '$e' => 'strict-array{0: strict-array{5: int, c: int}}', ], ], 'updateStringIntKeyWithTKeyedArrayRootAndNumberOffset' => [ @@ -573,7 +573,7 @@ function test(array $arg): array { $a["root"]["a"] = 5; $a["root"][0] = 3;', 'assertions' => [ - '$a' => 'array{root: array{0: int, a: int}}', + '$a' => 'strict-array{root: strict-array{0: int, a: int}}', ], ], 'updateStringIntKeyWithTKeyedArrayRoot' => [ @@ -601,10 +601,10 @@ function test(array $arg): array { $e["root"][$int] = 3; $e["root"][$string] = 5;', 'assertions' => [ - '$b' => 'array{root: array{0: int, c: int}}', - '$c' => 'array{root: array{0: int, c: int}}', - '$d' => 'array{root: array{5: int, a: int}}', - '$e' => 'array{root: array{5: int, c: int}}', + '$b' => 'strict-array{root: strict-array{0: int, c: int}}', + '$c' => 'strict-array{root: strict-array{0: int, c: int}}', + '$d' => 'strict-array{root: strict-array{5: int, a: int}}', + '$e' => 'strict-array{root: strict-array{5: int, c: int}}', ], ], 'mixedArrayAssignmentWithStringKeys' => [ @@ -661,9 +661,9 @@ function getThings(): array { $a["d"]["e"] = 5;', 'assertions' => [ '$a[\'b\']' => 'int', - '$a[\'d\']' => 'array{e: int}', + '$a[\'d\']' => 'strict-array{e: int}', '$a[\'d\'][\'e\']' => 'int', - '$a' => 'array{b: int, d: array{e: int}}', + '$a' => 'strict-array{b: int, d: strict-array{e: int}}', ], ], 'changeTKeyedArrayTypeInIf' => [ @@ -682,8 +682,8 @@ function getThings(): array { $a["b"]["e"] = "d";', 'assertions' => [ - '$a' => 'array{b: array{e: string}}', - '$a[\'b\']' => 'array{e: string}', + '$a' => 'strict-array{b: strict-array{e: string}}', + '$a[\'b\']' => 'strict-array{e: string}', '$a[\'b\'][\'e\']' => 'string', ], ], @@ -807,7 +807,7 @@ public function offsetSet($offset, $value): void $a = null; $a[0][] = 1;', 'assertions' => [ - '$a' => 'array{0: non-empty-list}', + '$a' => 'strict-array{0: non-empty-list}', ], 'ignored_issues' => ['PossiblyNullArrayAssignment'], ], @@ -845,7 +845,7 @@ public function offsetSet($offset, $value): void $a_values = array_values($a); $a_keys = array_keys($a);', 'assertions' => [ - '$a' => 'array{string, int}', + '$a' => 'strict-list{string, int}', '$a_values' => 'non-empty-list', '$a_keys' => 'non-empty-list', ], @@ -855,7 +855,7 @@ public function offsetSet($offset, $value): void $b = ["hello", 5]; $b[0] = 3;', 'assertions' => [ - '$b' => 'array{int, int}', + '$b' => 'strict-list{int, int}', ], ], 'changeIntOffsetKeyValuesAfterCopy' => [ @@ -864,8 +864,8 @@ public function offsetSet($offset, $value): void $c = $b; $c[0] = 3;', 'assertions' => [ - '$b' => 'array{string, int}', - '$c' => 'array{int, int}', + '$b' => 'strict-list{string, int}', + '$c' => 'strict-list{int, int}', ], ], 'mergeIntOffsetValues' => [ @@ -873,8 +873,8 @@ public function offsetSet($offset, $value): void $d = array_merge(["hello", 5], []); $e = array_merge(["hello", 5], ["hello again"]);', 'assertions' => [ - '$d' => 'array{0: string, 1: int}', - '$e' => 'array{0: string, 1: int, 2: string}', + '$d' => 'strict-list{string, int}', + '$e' => 'strict-list{string, int, string}', ], ], 'addIntOffsetToEmptyArray' => [ @@ -882,14 +882,14 @@ public function offsetSet($offset, $value): void $f = []; $f[0] = "hello";', 'assertions' => [ - '$f' => 'array{0: string}', + '$f' => 'strict-array{0: string}', ], ], 'dontIncrementIntOffsetForKeyedItems' => [ 'code' => ' 2, 3];', 'assertions' => [ - '$a' => 'array{0: int, 1: int, a: int}', + '$a' => 'strict-array{0: int, 1: int, a: int}', ], ], 'assignArrayOrSetNull' => [ @@ -1013,7 +1013,7 @@ function updateArray(array $arr) : array { $a = (array) (rand(0, 1) ? [1 => "one"] : 0); $b = (array) null;', 'assertions' => [ - '$a' => 'array{0?: int, 1?: string}', + '$a' => 'strict-array{0?: int, 1?: string}', '$b' => 'array', ], ], @@ -1154,8 +1154,8 @@ function takesList(array $arr) : void {} $b[] = rand(0, 10);', 'assertions' => [ - '$a' => 'array{int, int, int}', - '$b' => 'array{int, int, int, int<0, 10>}', + '$a' => 'strict-list{int, int, int}', + '$b' => 'strict-list{int, int, int, int<0, 10>}', ], ], 'listMergedWithTKeyedArrayList' => [ @@ -1240,9 +1240,9 @@ function foo(array $arr) : string { $arr2 = [...$arr1]; $arr3 = [1 => 0, ...$arr1];', 'assertions' => [ - '$result' => 'array{int, int, int, int, int, int, int, int}', - '$arr2' => 'array{int, int, int}', - '$arr3' => 'array{1: int, 2: int, 3: int, 4: int}', + '$result' => 'strict-list{int, int, int, int, int, int, int, int}', + '$arr2' => 'strict-list{int, int, int}', + '$arr3' => 'strict-array{1: int, 2: int, 3: int, 4: int}', ] ], 'arraySpreadWithString' => [ @@ -1253,7 +1253,7 @@ function foo(array $arr) : string { ...["b" => 2] ];', 'assertions' => [ - '$x===' => 'array{a: 1, b: 2}', + '$x===' => 'strict-array{a: 1, b: 2}', ], 'ignored_issues' => [], 'php_version' => '8.1' @@ -1274,7 +1274,7 @@ public function override(int $offset): void { 'propertyAssignmentToTKeyedArrayIntKeys' => [ 'code' => ' [ 'code' => ' "c", "b" => "d"]; public function append(string $str) : void { @@ -1408,8 +1408,8 @@ function run1(array $arguments): void { 'assignWithLiteralStringKey' => [ 'code' => ' $i - * @return array + * @param array $i + * @return array */ function addOneEntry(array $i, int $id): array { $i[$id][rand(0, 1) ? "internal" : "ported"] = true; @@ -1427,7 +1427,7 @@ function (string $x) { $a += ["e" => new RuntimeException()];', 'assertions' => [ - '$a' => 'array{c: RuntimeException, e: RuntimeException}', + '$a' => 'strict-array{c: RuntimeException, e: RuntimeException}', ] ], 'mergeArrayKeysProperly' => [ @@ -1554,7 +1554,7 @@ function unpackIterable(Traversable $data): array $e = [...$a, ...$b, ...$c, ...$d, 3]; ', - 'assertions' => ['$e===' => 'array{1, 2, 3}'], + 'assertions' => ['$e===' => 'strict-list{1, 2, 3}'], ], 'unpackArrayCanBeEmpty' => [ 'code' => ' ['$_a===' => 'array{16: 16, 17: 17, 18: 18}'] + 'assertions' => ['$_a===' => 'strict-array{16: 16, 17: 17, 18: 18}'] ], 'unpackTypedIterableWithStringKeysIntoArray' => [ 'code' => ' [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ArrayFunctionCallTest.php b/tests/ArrayFunctionCallTest.php index aa8ca206ca1..2ff1985ba1e 100644 --- a/tests/ArrayFunctionCallTest.php +++ b/tests/ArrayFunctionCallTest.php @@ -13,7 +13,7 @@ class ArrayFunctionCallTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -28,7 +28,7 @@ function(?int $i): bool { } );', 'assertions' => [ - '$d' => 'array{a?: int<1, 10>, b?: int<1, 10>}', + '$d' => 'strict-array{a?: int<1, 10>, b?: int<1, 10>}', '$e' => 'array|null>', ], ], @@ -39,7 +39,7 @@ function(?int $i): bool { * @param positive-int $positiveOne * @param int<0,12> $d * @param int<1,12> $f - * @psalm-return array{a: numeric, b?: int, c: positive-int, d?: int<0, 12>, f: int<1,12>} + * @psalm-return strict-array{a: numeric, b?: int, c: positive-int, d?: int<0, 12>, f: int<1,12>} */ function makeAList($a, int $anyInt, int $positiveOne, int $d, int $f): array { return array_filter(["a" => "1", "b" => $anyInt, "c" => $positiveOne, "d" => $d, "f" => $f]); @@ -200,9 +200,9 @@ public static function isStatus(string $role): bool 'arrayCombineDynamicParams' => [ 'code' => ' */ - function getStrings(): array{ return []; } + function getStrings(): array { return []; } /** @return array */ - function getInts(): array{ return []; } + function getInts(): array { return []; } $c = array_combine(getStrings(), getInts());', 'assertions' => [ '$c' => 'array|false', @@ -212,14 +212,14 @@ function getInts(): array{ return []; } 'code' => ' [ - '$d' => 'array{0: string, 1: string, 2: string, 3: string, 4: int, 5: int, 6: int}', + '$d===' => "strict-list{'a', 'b', 'c', 'd', 1, 2, 3}", ], ], 'arrayMergePossiblyUndefined' => [ 'code' => ' 5], $opts); @@ -248,8 +248,8 @@ function foo(array $list) : array { 'arrayMergeTypes' => [ 'code' => ' ' [ - '$d' => 'array{0: int, 1: int, 2: int, 3: string}', + '$d===' => "strict-list{1, 2, 3, 'd'}", ], ], 'arrayReplacePossiblyUndefined' => [ 'code' => ' 5], $opts); @@ -304,8 +304,8 @@ function foo(array $list) : array { 'arrayReplaceTypes' => [ 'code' => ' [ 'code' => '|array{null} $arr + * @param non-empty-list|strict-array{null} $arr * @return array */ function foo(array $arr) { @@ -723,7 +723,7 @@ function foo($a) foo($a3);', 'assertions' => [ - '$a3' => 'array{hi: int, bye: int}', + '$a3' => 'strict-array{bye: int, hi: int}', ], ], 'arrayReplaceTKeyedArray' => [ @@ -743,7 +743,7 @@ function foo($a) foo($a3);', 'assertions' => [ - '$a3' => 'array{hi: int, bye: int}', + '$a3' => 'strict-array{bye: int, hi: int}', ], ], 'arrayRand' => [ @@ -755,10 +755,10 @@ function foo($a) $e = array_rand($more_vars);', 'assertions' => [ - '$vars' => 'array{x: string, y: string}', + '$vars' => 'strict-array{x: string, y: string}', '$c' => 'string', '$d' => 'string', - '$more_vars' => 'array{string, string}', + '$more_vars' => 'strict-list{string, string}', '$e' => 'int', ], ], @@ -772,7 +772,7 @@ function foo($a) $f = array_rand($vars, $b);', 'assertions' => [ - '$vars' => 'array{x: string, y: string}', + '$vars' => 'strict-array{x: string, y: string}', '$c' => 'string', '$e' => 'list', '$f' => 'list|string', @@ -797,7 +797,7 @@ function test(): void { function expectsInt(int $a) : void {} /** - * @param array $list + * @param array $list */ function test(array $list) : void { @@ -913,7 +913,7 @@ function foo(array $v): array { 'arrayMapTKeyedArrayAndCallable' => [ 'code' => ' 1, "key2"=> "2"]; @@ -936,7 +936,7 @@ function takesList(array $list): void {} 'arrayMapTKeyedArrayAndClosure' => [ 'code' => ' 1, "key2"=> "2"]; @@ -1241,7 +1241,7 @@ function makeArray(): array { return []; } ], 'arrayResetMaybeEmptyTKeyedArray' => [ 'code' => ' [ 'code' => '> */ function makeGenericArray(): array { return []; } - /** @return array */ + /** @return array */ function makeShapeArray(): array { return []; } - /** @return array */ + /** @return array */ function makeUnionArray(): array { return []; } - /** @return array */ + /** @return array */ function makeKeyedArray(): array { return []; } $a = array_column([[1], [2], [3]], 0); $b = array_column([["a" => 1], ["a" => 2], ["a" => 3]], "a"); @@ -1358,8 +1358,8 @@ function makeKeyedArray(): array { return []; } 'assertions' => [ '$a' => 'non-empty-list', '$b' => 'non-empty-list', - '$c' => 'array', - '$d' => 'array', + '$c' => 'array', + '$d' => 'array', '$e' => 'array', '$f' => 'non-empty-array', '$g' => 'list', @@ -1528,8 +1528,8 @@ function (int $carry, int $item) { array_splice($a, rand(-10, 0), rand(0, 10), $b);', 'assertions' => [ '$a' => 'non-empty-list', - '$b' => 'array{string, string, string}', - '$c' => 'array{int, int, int}', + '$b' => 'strict-list{string, string, string}', + '$c' => 'strict-list{int, int, int}', ], ], 'arraySpliceReturn' => [ @@ -1545,7 +1545,7 @@ function (int $carry, int $item) { $d = [["red"], ["green"], ["blue"]]; array_splice($d, -1, 1, "foo");', 'assertions' => [ - '$d' => 'array', + '$d' => 'array', ], ], 'ksortPreserveShape' => [ @@ -1555,7 +1555,7 @@ function (int $carry, int $item) { acceptsAShape($a); /** - * @param array{a:int,b:int} $a + * @param strict-array{a:int,b:int} $a */ function acceptsAShape(array $a): void {}', ], @@ -1680,7 +1680,7 @@ function getSize(): int { return random_int(1, 10); } ], 'arrayPadMixed' => [ 'code' => ' [ 'code' => ' $list */ $b = array_chunk($list, 2); @@ -1723,7 +1723,7 @@ function getSize(): int { return random_int(1, 10); } ], 'arrayChunkPreservedKeys' => [ 'code' => ' $list */ $b = array_chunk($list, 2, true); @@ -1745,7 +1745,7 @@ function getSize(): int { return random_int(1, 10); } ], 'arrayChunkMixed' => [ 'code' => ' $list */ $b = array_chunk($list, 2); @@ -1781,7 +1781,7 @@ function foo(array $strings): array { 'SKIPPED-arrayMapZip' => [ 'code' => ' + * @return array */ function getCharPairs(string $line) : array { $chars = str_split($line); @@ -1972,6 +1972,19 @@ function takesString(string $string): void {} takesString($a[0]); array_map("takesString", $a);', ], + 'arrayMapZip' => [ + 'code' => ' [ + '$d===' => "strict-list{strict-list{1, 'one', 'uno'}, strict-list{2, 'two', 'dos'}, strict-list{3, 'three', 'tres'}, strict-list{4, 'four', 'cuatro'}, strict-list{5, 'five', 'cinco'}, strict-list{null, null, 'seis'}}" + ], + 'ignored_issues' => [], + 'php_version' => '7.4', + ], 'arrayMapExplicitZip' => [ 'code' => ' [ 'code' => ' $b * * @return array{A: int} @@ -2041,7 +2054,7 @@ function merger(array $a, array $b) : array { 'arrayMergeKeepFirstKeysSameType' => [ 'code' => ' $b * * @return array{A: int} @@ -2053,7 +2066,7 @@ function merger(array $a, array $b) : array { 'arrayReplaceKeepLastKeysAndType' => [ 'code' => ' $b * * @return array{A: int} @@ -2065,7 +2078,7 @@ function merger(array $a, array $b) : array { 'arrayReplaceKeepFirstKeysSameType' => [ 'code' => ' $b * * @return array{A: int} @@ -2103,7 +2116,7 @@ function doStuff($lengths): void { ], 'countOnListIntoTuple' => [ 'code' => ' $list */ @@ -2116,7 +2129,7 @@ function bar(array $list) : void { 'arrayColumnwithKeyedArrayWithoutRedundantUnion' => [ 'code' => ' $foos + * @param array $foos */ function foo(array $foos): void { array_multisort($formLayoutFields, SORT_ASC, array_column($foos, "y")); @@ -2154,7 +2167,7 @@ function getLine(): array { return ["a", "b"]; } $line = getLine(); - if (empty($line[0])) { // converts array to array{0:string} + if (empty($line[0])) { // converts array to strict-array{0:string} throw new InvalidArgumentException; } @@ -2203,7 +2216,7 @@ public function handle(): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -2351,7 +2364,7 @@ function (int $carry, int $item) : stdClass { function expectsInt(int $a) : void {} /** - * @param array $list + * @param array $list */ function test(array $list) : void { @@ -2408,7 +2421,7 @@ function (string $a, string $b): int { 'arrayMergeKeepFirstKeysButNotType' => [ 'code' => ' $b * * @return array{A: int} @@ -2421,7 +2434,7 @@ function merger(array $a, array $b) : array { 'arrayReplaceKeepFirstKeysButNotType' => [ 'code' => ' $b * * @return array{A: int} diff --git a/tests/ArrayKeysTest.php b/tests/ArrayKeysTest.php index 0e2185e4c36..7fba8bb6bc4 100644 --- a/tests/ArrayKeysTest.php +++ b/tests/ArrayKeysTest.php @@ -13,7 +13,7 @@ class ArrayKeysTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -104,7 +104,7 @@ function getKey() { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/AssertAnnotationTest.php b/tests/AssertAnnotationTest.php index adc2fc39fe8..3edba341484 100644 --- a/tests/AssertAnnotationTest.php +++ b/tests/AssertAnnotationTest.php @@ -91,7 +91,7 @@ function requiresString(string $_str): void {} } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -995,7 +995,7 @@ function f($_p): bool { 'assertDifferentTypeOfArray' => [ 'code' => ' [ 'code' => ', - * env?: array, * buildDeps?: list, * configure?: string @@ -1247,12 +1247,12 @@ function assertStructure($data): void {}' 'intersectArraysAfterAssertion' => [ 'code' => ' [], 'ignored_issues' => [], - '7.4' + 'php_version' => '7.4' ], 'onPropertyOfImmutableArgument' => [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/AssignmentTest.php b/tests/AssignmentTest.php index 29749fcbf1c..76d047fbe8d 100644 --- a/tests/AssignmentTest.php +++ b/tests/AssignmentTest.php @@ -10,9 +10,6 @@ class AssignmentTest extends TestCase use InvalidCodeAnalysisTestTrait; use ValidCodeAnalysisTestTrait; - /** - * @return iterable,ignored_issues?:list}> - */ public function providerValidCodeParse(): iterable { return [ @@ -98,7 +95,7 @@ function foo2(?string &$u, ?string &$v): void {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/AttributeTest.php b/tests/AttributeTest.php index 7318ecabaaf..717a61a6ed7 100644 --- a/tests/AttributeTest.php +++ b/tests/AttributeTest.php @@ -36,9 +36,6 @@ class Foo {} $this->analyzeFile('somefile.php', new Context()); } - /** - * @return iterable,ignored_issues?:list}> - */ public function providerValidCodeParse(): iterable { return [ @@ -437,7 +434,7 @@ public function __construct() {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/BinaryOperationTest.php b/tests/BinaryOperationTest.php index b7eb0153e60..0b1dfd16a7b 100644 --- a/tests/BinaryOperationTest.php +++ b/tests/BinaryOperationTest.php @@ -149,7 +149,7 @@ public function testMatchOnBoolean(): void { $config = Config::getInstance(); $config->strict_binary_operands = true; - + $this->addFile( 'somefile.php', 'analyzeFile('somefile.php', new Context()); } - /** - * @return iterable,ignored_issues?:list,php_version?:string}> - */ public function providerValidCodeParse(): iterable { return [ @@ -630,8 +627,8 @@ function foo(array $arr) : void { 'addArrays' => [ 'code' => ' 5]; @@ -996,7 +993,7 @@ function toPositiveInt(int $i): int } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/CallableTest.php b/tests/CallableTest.php index 5e09cbf8b5c..6c95e9a2ff2 100644 --- a/tests/CallableTest.php +++ b/tests/CallableTest.php @@ -10,9 +10,6 @@ class CallableTest extends TestCase use InvalidCodeAnalysisTestTrait; use ValidCodeAnalysisTestTrait; - /** - * @return iterable,ignored_issues?:list}> - */ public function providerValidCodeParse(): iterable { return [ @@ -132,7 +129,7 @@ public function map(Closure $ab): ArrayList /** * @template T * @param ArrayList $list - * @return ArrayList + * @return ArrayList */ function asTupled(ArrayList $list): ArrayList { @@ -144,7 +141,7 @@ function asTupled(ArrayList $list): ArrayList $a = new ArrayList(); $b = asTupled($a);', 'assertions' => [ - '$b' => 'ArrayList', + '$b' => 'ArrayList', ], ], 'inferArgByPreviousFunctionArg' => [ @@ -430,8 +427,8 @@ function pipe(array $_a, callable $_ab): array '$result1' => 'list', '$result2' => 'list', ], - 'error_levels' => [], - '8.0', + 'ignored_issues' => [], + 'php_version' => '8.0', ], 'inferPipelineWithPartiallyAppliedFunctions' => [ 'code' => ' [ '$result' => 'non-empty-list>|null', ], - 'error_levels' => [], - '8.0', + 'ignored_issues' => [], + 'php_version' => '8.0', ], 'varReturnType' => [ 'code' => ' [ - '$a' => 'array{string, string}', - '$b' => 'array{string, string}', - '$c' => 'array{string, string}', - '$d' => 'array{string, string}', - '$e' => 'array{string, string}', - '$f' => 'array{string, string}', + '$a' => 'strict-list{string, string}', + '$b' => 'strict-list{string, string}', + '$c' => 'strict-list{string, string}', + '$d' => 'strict-list{string, string}', + '$e' => 'strict-list{string, string}', + '$f' => 'strict-list{string, string}', ], ], 'arrayCallableMethod' => [ @@ -1334,7 +1331,7 @@ abstract class TestClass { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -1720,7 +1717,7 @@ public function map(Closure $effect): ArrayList * @template B * * @param ArrayList $list - * @return ArrayList + * @return ArrayList */ function genericContext(ArrayList $list): ArrayList { diff --git a/tests/CastTest.php b/tests/CastTest.php index 2d20c4d3f02..43397273ea1 100644 --- a/tests/CastTest.php +++ b/tests/CastTest.php @@ -8,9 +8,6 @@ class CastTest extends TestCase { use ValidCodeAnalysisTestTrait; - /** - * @return iterable,ignored_issues?:list,php_version?:string}> - */ public function providerValidCodeParse(): iterable { yield 'SKIPPED-castFalseOrIntToInt' => [ diff --git a/tests/CheckTypeTest.php b/tests/CheckTypeTest.php index d6ca6e3f652..5178f4ffe7e 100644 --- a/tests/CheckTypeTest.php +++ b/tests/CheckTypeTest.php @@ -10,9 +10,6 @@ class CheckTypeTest extends TestCase use InvalidCodeAnalysisTestTrait; use ValidCodeAnalysisTestTrait; - /** - * @return iterable,ignored_issues?:list,php_version?:string}> - */ public function providerValidCodeParse(): iterable { yield 'allowSubtype' => [ @@ -23,9 +20,6 @@ public function providerValidCodeParse(): iterable ]; } - /** - * @return iterable,php_version?:string}> - */ public function providerInvalidCodeParse(): iterable { yield 'checkType' => [ diff --git a/tests/ClassLikeStringTest.php b/tests/ClassLikeStringTest.php index 8541c05e746..5a2213a0408 100644 --- a/tests/ClassLikeStringTest.php +++ b/tests/ClassLikeStringTest.php @@ -53,9 +53,6 @@ public static function foo() : void {} $this->analyzeFile('somefile.php', new Context()); } - /** - * @return iterable,ignored_issues?:list}> - */ public function providerValidCodeParse(): iterable { return [ @@ -82,7 +79,7 @@ class A {} class B {} takesClassConstants(["A", "B"]);', - 'annotations' => [], + 'assertions' => [], 'ignored_issues' => ['ArgumentTypeCoercion'], ], 'singleClassConstantAsConstant' => [ @@ -107,7 +104,7 @@ class A {} /** @psalm-suppress ArgumentTypeCoercion */ takesClassConstants("A");', - 'annotations' => [], + 'assertions' => [], ], 'returnClassConstant' => [ 'code' => ' [], + 'assertions' => [], 'ignored_issues' => ['LessSpecificReturnStatement', 'MoreSpecificReturnType'], ], 'returnClassConstantArray' => [ @@ -156,7 +153,7 @@ class B {} function takesClassConstants() : array { return ["A", "B"]; }', - 'annotations' => [], + 'assertions' => [], 'ignored_issues' => ['LessSpecificReturnStatement', 'MoreSpecificReturnType'], ], 'ifClassStringEquals' => [ @@ -815,12 +812,12 @@ function takesString(string $s) { 'code' => ' $s */ function takesAString(string $a): void {} /** @param class-string $s */ function takesBString(string $a): void {} - + /** @param class-string $s */ function foo(string $s): void { if (is_subclass_of($s, A::class)) { @@ -840,7 +837,7 @@ class B {} function takesAString(string $a): void {} /** @param class-string $s */ function takesBString(string $a): void {} - + function foo(string $s): void { if (!class_exists($s, false)) { return; @@ -866,7 +863,7 @@ function foo(A $a): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ClassLoadOrderTest.php b/tests/ClassLoadOrderTest.php index 4dc07fa149e..706df50b735 100644 --- a/tests/ClassLoadOrderTest.php +++ b/tests/ClassLoadOrderTest.php @@ -10,9 +10,6 @@ class ClassLoadOrderTest extends TestCase use InvalidCodeAnalysisTestTrait; use ValidCodeAnalysisTestTrait; - /** - * @return iterable,ignored_issues?:list}> - */ public function providerValidCodeParse(): iterable { return [ @@ -123,7 +120,7 @@ class C extends B { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ClassScopeTest.php b/tests/ClassScopeTest.php index 1047a5b5345..c00d1c590e2 100644 --- a/tests/ClassScopeTest.php +++ b/tests/ClassScopeTest.php @@ -10,9 +10,6 @@ class ClassScopeTest extends TestCase use InvalidCodeAnalysisTestTrait; use ValidCodeAnalysisTestTrait; - /** - * @return iterable,ignored_issues?:list}> - */ public function providerValidCodeParse(): iterable { return [ @@ -154,7 +151,7 @@ protected static function foo(): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ClassTest.php b/tests/ClassTest.php index 609eec1e552..580622b9ea5 100644 --- a/tests/ClassTest.php +++ b/tests/ClassTest.php @@ -40,7 +40,7 @@ public function real_escape_string(string $string) } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -628,7 +628,7 @@ public function stabilize(): self { return $this; } } - + class a { /** * @return iter @@ -639,7 +639,7 @@ public function ret(): iter { } class b extends a { } - + $a = new b; $a = $a->ret(); $a = $a->stabilize();', @@ -663,7 +663,7 @@ public function stabilize(): self { return $this; } } - + interface a { /** * @return iter @@ -675,7 +675,7 @@ public function ret(): iter { return new iter([$this]); } } - + /** @var a */ $a = new b; $a = $a->ret(); @@ -700,7 +700,7 @@ public function stabilize(): self { return $this; } } - + interface a { /** * @return iter @@ -712,7 +712,7 @@ public function ret(): iter { return new iter([$this]); } } - + /** @var a */ $a = new b; $a = $a->ret(); @@ -754,7 +754,7 @@ public function valid() { return false; } /** * @template TKey * @template TValue - * + * * @implements Traversable */ abstract class C implements Traversable {} @@ -773,7 +773,7 @@ abstract class C implements I {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -1121,7 +1121,7 @@ class Foo /** @var class-string */ protected const CLASS = Bar::class; } - + class Bar {} ', 'error_message' => 'ReservedWord', diff --git a/tests/CloneTest.php b/tests/CloneTest.php index 6afe54b5afc..6b19178a337 100644 --- a/tests/CloneTest.php +++ b/tests/CloneTest.php @@ -13,7 +13,7 @@ class CloneTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -49,7 +49,7 @@ public function foo(): self { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -88,7 +88,7 @@ class a { private function __clone() {} } class b extends a {} - + clone new b;', 'error_message' => 'InvalidClone', ], @@ -100,7 +100,7 @@ private function __clone() {} class b { use a; } - + clone new b;', 'error_message' => 'InvalidClone', ], diff --git a/tests/ClosureTest.php b/tests/ClosureTest.php index 53b3154cebc..68e79d7204f 100644 --- a/tests/ClosureTest.php +++ b/tests/ClosureTest.php @@ -13,7 +13,7 @@ class ClosureTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -98,7 +98,7 @@ function(string $a) { $mirror = function(int $i) : int { return $i; }; $a = array_map($mirror, [1, 2, 3]);', 'assertions' => [ - '$a' => 'array{int, int, int}', + '$a' => 'strict-list{int, int, int}', ], ], 'inlineCallableFunction' => [ @@ -263,14 +263,14 @@ function(C $c) : B { return new B;} 'inferArrayMapReturnTypeWithoutTypehints' => [ 'code' => ' [ 'code' => ' [ - '$result' => 'array{stdClass}' + '$result' => 'strict-list{stdClass}' ], ], 'CallableWithArrayReduce' => [ @@ -785,8 +785,8 @@ function takesIntToString(\Closure $_): void {} takesIntToString(C::foo(...));', 'assertions' => [], - [], - '8.1', + 'ignored_issues' => [], + 'php_version' => '8.1', ], 'FirstClassCallable:InheritedStaticMethodWithStaticTypeParameter' => [ 'code' => ' [ - '$result1' => 'array{null, null, null}', - '$result2' => 'array{string, string, string}', - '$result3' => 'array{int, int, int}', + '$result1' => 'strict-list{null, null, null}', + '$result2' => 'strict-list{string, string, string}', + '$result3' => 'strict-list{int, int, int}', ], 'ignored_issues' => [], 'php_version' => '8.1' @@ -906,7 +906,7 @@ private function handler(): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/CodebaseTest.php b/tests/CodebaseTest.php index 4083e836a0e..2900ebd80bb 100644 --- a/tests/CodebaseTest.php +++ b/tests/CodebaseTest.php @@ -51,7 +51,7 @@ public function isTypeContainedByType(string $input, string $container, bool $ex ); } - /** @return iterable */ + /** @return iterable */ public function typeContainments(): iterable { yield ['int', 'int|string', true]; @@ -81,7 +81,7 @@ public function canTypeBeContainedByType(string $input, string $container, bool ); } - /** @return iterable */ + /** @return iterable */ public function typeIntersections(): iterable { yield ['int', 'int|string', true]; @@ -95,7 +95,7 @@ public function typeIntersections(): iterable * @test * @dataProvider iterableParams * - * @param array{string,string} $expected + * @param strict-array{string,string} $expected * */ public function getKeyValueParamsForTraversableObject(string $input, array $expected): void @@ -120,7 +120,7 @@ public function getKeyValueParamsForTraversableObject(string $input, array $expe ); } - /** @return iterable */ + /** @return iterable */ public function iterableParams(): iterable { yield ['iterable', ['int', 'string']]; diff --git a/tests/Config/ConfigTest.php b/tests/Config/ConfigTest.php index 442cdbfa600..7b8fcff8b54 100644 --- a/tests/Config/ConfigTest.php +++ b/tests/Config/ConfigTest.php @@ -1127,10 +1127,10 @@ public function testGlobals(): void - + - + ' ) @@ -1424,7 +1424,7 @@ public function testInferPropertyTypesFromConstructorIsRead(): void } /** - * @return array + * @return array */ public function pluginRegistersScannerAndAnalyzerDataProvider(): array { diff --git a/tests/Config/PluginTest.php b/tests/Config/PluginTest.php index 1fd197ec02a..a30824ffc0f 100644 --- a/tests/Config/PluginTest.php +++ b/tests/Config/PluginTest.php @@ -1016,7 +1016,7 @@ public function testFunctionDynamicStorageProviderHook(): void function custom_array_map(...$_args) { throw new RuntimeException("???"); } /** - * @param list $_list + * @param list $_list */ function acceptsList(array $_list): void { } diff --git a/tests/ConstValuesTest.php b/tests/ConstValuesTest.php index 4358c0367f7..643e69d8444 100644 --- a/tests/ConstValuesTest.php +++ b/tests/ConstValuesTest.php @@ -11,7 +11,7 @@ class ConstValuesTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -109,7 +109,7 @@ class C {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ConstantTest.php b/tests/ConstantTest.php index c7152b43287..7c8615409df 100644 --- a/tests/ConstantTest.php +++ b/tests/ConstantTest.php @@ -84,7 +84,7 @@ function bar(): Bar } /** - * @return iterable,ignored_issues?:list, php_version?: string}> + * */ public function providerValidCodeParse(): iterable { @@ -308,10 +308,12 @@ class B { 'lateConstantResolutionParentArrayPlus' => [ 'code' => ' true]; } class B extends A { + /** @var array{a: true, b: true} */ public const ARR = parent::ARR + ["b" => true]; } @@ -319,7 +321,7 @@ class C extends B { public const ARR = parent::ARR + ["c" => true]; } - /** @param array{a: true, b: true, c: true} $arg */ + /** @param strict-array{a: true, b: true, c: true} $arg */ function foo(array $arg): void {} foo(C::ARR); ', @@ -327,10 +329,12 @@ function foo(array $arg): void {} 'lateConstantResolutionParentArraySpread' => [ 'code' => ' [ - '$arr===' => 'array{1, 2}', + '$arr===' => 'strict-list{1, 2}', ], ], 'keysInUnpackedArrayAreReset' => [ @@ -1275,7 +1279,7 @@ class C { $arr = C::A; ', 'assertions' => [ - '$arr===' => 'array{2}', + '$arr===' => 'strict-list{2}', ], ], 'arrayKeysSequenceContinuesAfterExplicitIntKey' => [ @@ -1286,7 +1290,7 @@ class C { $arr = C::A; ', 'assertions' => [ - '$arr===' => "array{10: 'aa', 11: 'zz', 5: 'a', 6: 'z'}", + '$arr===' => "strict-array{10: 'aa', 11: 'zz', 5: 'a', 6: 'z'}", ], ], 'arrayKeysSequenceContinuesAfterNonIntKey' => [ @@ -1297,7 +1301,7 @@ class C { $arr = C::A; ', 'assertions' => [ - '$arr===' => "array{5: 'a', 6: 'aa', zz: 'z'}", + '$arr===' => "strict-array{5: 'a', 6: 'aa', zz: 'z'}", ], ], 'unresolvedConstWithUnaryMinus' => [ @@ -1524,7 +1528,7 @@ class Baz implements Foo } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/CoreStubsTest.php b/tests/CoreStubsTest.php index 5a60a348b5d..3d76a0d343a 100644 --- a/tests/CoreStubsTest.php +++ b/tests/CoreStubsTest.php @@ -8,9 +8,6 @@ class CoreStubsTest extends TestCase { use ValidCodeAnalysisTestTrait; - /** - * @return iterable,ignored_issues?:list,php_version?:string}> - */ public function providerValidCodeParse(): iterable { yield 'RecursiveArrayIterator::CHILD_ARRAYS_ONLY (#6464)' => [ @@ -49,7 +46,7 @@ public function providerValidCodeParse(): iterable '$period' => 'DatePeriod', '$dt' => 'DateTimeInterface|null' ], - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '7.3', ]; yield 'Iterating over \DatePeriod (#5954) PHP8 IteratorAggregate' => [ @@ -68,7 +65,7 @@ public function providerValidCodeParse(): iterable '$period' => 'DatePeriod', '$dt' => 'DateTimeImmutable|null' ], - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '8.0', ]; yield 'Iterating over \DatePeriod (#5954), ISO string' => [ @@ -83,7 +80,7 @@ public function providerValidCodeParse(): iterable '$period' => 'DatePeriod', '$dt' => 'DateTime|null' ], - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '8.0', ]; yield 'DatePeriod implements only Traversable on PHP 7' => [ @@ -92,7 +89,7 @@ public function providerValidCodeParse(): iterable $period = new DatePeriod("R4/2012-07-01T00:00:00Z/P7D"); if ($period instanceof IteratorAggregate) {}', 'assertions' => [], - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '7.3', ]; yield 'DatePeriod implements IteratorAggregate on PHP 8' => [ @@ -101,7 +98,7 @@ public function providerValidCodeParse(): iterable $period = new DatePeriod("R4/2012-07-01T00:00:00Z/P7D"); if ($period instanceof IteratorAggregate) {}', 'assertions' => [], - 'error_levels' => ['RedundantCondition'], + 'ignored_issues' => ['RedundantCondition'], 'php_version' => '8.0', ]; } diff --git a/tests/DateTimeTest.php b/tests/DateTimeTest.php index d5ae0d50d36..25a69376e4a 100644 --- a/tests/DateTimeTest.php +++ b/tests/DateTimeTest.php @@ -8,9 +8,6 @@ class DateTimeTest extends TestCase { use ValidCodeAnalysisTestTrait; - /** - * @return array, error_levels?: list}> - */ public function providerValidCodeParse(): iterable { return [ diff --git a/tests/DeprecatedAnnotationTest.php b/tests/DeprecatedAnnotationTest.php index 02f21cc9914..218b716a128 100644 --- a/tests/DeprecatedAnnotationTest.php +++ b/tests/DeprecatedAnnotationTest.php @@ -11,7 +11,7 @@ class DeprecatedAnnotationTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -106,7 +106,7 @@ class A { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/DocblockInheritanceTest.php b/tests/DocblockInheritanceTest.php index 6a1762a3148..e532185ae88 100644 --- a/tests/DocblockInheritanceTest.php +++ b/tests/DocblockInheritanceTest.php @@ -11,7 +11,7 @@ class DocblockInheritanceTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -154,7 +154,7 @@ function takesF(F $f) : B { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/DocumentationTest.php b/tests/DocumentationTest.php index 8dd2a40cb0b..190e6200388 100644 --- a/tests/DocumentationTest.php +++ b/tests/DocumentationTest.php @@ -203,11 +203,11 @@ public function testAllIssuesCovered(): void * * @param string $code * @param string $error_message - * @param array $error_levels + * @param array $ignored_issues * @param bool $check_references * */ - public function testInvalidCode($code, $error_message, $error_levels = [], $check_references = false, string $php_version = '8.0'): void + public function testInvalidCode($code, $error_message, $ignored_issues = [], $check_references = false, string $php_version = '8.0'): void { if (strpos($this->getTestName(), 'SKIPPED-') !== false) { $this->markTestSkipped(); @@ -227,7 +227,7 @@ public function testInvalidCode($code, $error_message, $error_levels = [], $chec $this->project_analyzer->getConfig()->ensure_array_string_offsets_exist = $is_array_offset_test; $this->project_analyzer->getConfig()->ensure_array_int_offsets_exist = $is_array_offset_test; - foreach ($error_levels as $error_level) { + foreach ($ignored_issues as $error_level) { $this->project_analyzer->getCodebase()->config->setCustomErrorLevel($error_level, Config::REPORT_SUPPRESS); } @@ -253,7 +253,7 @@ public function testInvalidCode($code, $error_message, $error_levels = [], $chec } /** - * @return array + * @return array */ public function providerInvalidCodeParse(): array { @@ -370,7 +370,7 @@ public function testAllAnnotationsAreDocumented(string $annotation): void ); } - /** @return iterable */ + /** @return iterable */ public function knownAnnotations(): iterable { foreach (DocComment::PSALM_ANNOTATIONS as $annotation) { diff --git a/tests/EndToEnd/PsalmEndToEndTest.php b/tests/EndToEnd/PsalmEndToEndTest.php index e8e9ddb1ee5..7b6d1a7de87 100644 --- a/tests/EndToEnd/PsalmEndToEndTest.php +++ b/tests/EndToEnd/PsalmEndToEndTest.php @@ -233,7 +233,7 @@ public function testLegacyConfigWithoutresolveFromConfigFile(): void } /** - * @return array{STDOUT: string, STDERR: string, CODE: int|null} + * @return strict-array{STDOUT: string, STDERR: string, CODE: int|null} */ private function runPsalmInit(?int $level = null, ?string $php_version = null): array { diff --git a/tests/EndToEnd/PsalmRunnerTrait.php b/tests/EndToEnd/PsalmRunnerTrait.php index 75065071af7..e521d22bf21 100644 --- a/tests/EndToEnd/PsalmRunnerTrait.php +++ b/tests/EndToEnd/PsalmRunnerTrait.php @@ -17,7 +17,7 @@ trait PsalmRunnerTrait /** * @param list $args * - * @return array{STDOUT: string, STDERR: string, CODE: int|null} + * @return strict-array{STDOUT: string, STDERR: string, CODE: int|null} */ private function runPsalm( array $args, diff --git a/tests/EnumTest.php b/tests/EnumTest.php index 863acba0cbf..b65d840ffd7 100644 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -11,7 +11,7 @@ class EnumTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -444,7 +444,7 @@ function foo(): Code|null } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ExtendsFinalClassTest.php b/tests/ExtendsFinalClassTest.php index e9adcb8ef1a..a0fb1e67560 100644 --- a/tests/ExtendsFinalClassTest.php +++ b/tests/ExtendsFinalClassTest.php @@ -11,7 +11,7 @@ class ExtendsFinalClassTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -43,7 +43,7 @@ class B extends A {}' } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/FileDiffTest.php b/tests/FileDiffTest.php index 3b18fdb6900..f81732c9af6 100644 --- a/tests/FileDiffTest.php +++ b/tests/FileDiffTest.php @@ -61,9 +61,9 @@ public function testCode( $found_offsets = array_map( /** - * @param array{0: int, 1: int, 2: int, 3: int} $arr + * @param strict-array{0: int, 1: int, 2: int, 3: int} $arr * - * @return array{0: int, 1: int} + * @return strict-array{0: int, 1: int} */ fn(array $arr): array => [$arr[2], $arr[3]], $diff[3] @@ -80,7 +80,7 @@ public function testCode( * @param string[] $same_methods * @param string[] $same_signatures * @param string[] $changed_methods - * @param array $diff_map_offsets + * @param array $diff_map_offsets * */ public function testPartialAstDiff( @@ -135,9 +135,9 @@ public function testPartialAstDiff( $found_offsets = array_map( /** - * @param array{0: int, 1: int, 2: int, 3: int} $arr + * @param strict-array{0: int, 1: int, 2: int, 3: int} $arr * - * @return array{0: int, 1: int} + * @return strict-array{0: int, 1: int} */ fn(array $arr): array => [$arr[2], $arr[3]], $diff[3] @@ -204,7 +204,7 @@ private function assertTreesEqual(array $a, array $b): void } /** - * @return array,list}> + * @return array,list}> */ public function getChanges(): array { diff --git a/tests/FileManipulation/ClassConstantMoveTest.php b/tests/FileManipulation/ClassConstantMoveTest.php index 707664328a1..dbf9b7ccfb3 100644 --- a/tests/FileManipulation/ClassConstantMoveTest.php +++ b/tests/FileManipulation/ClassConstantMoveTest.php @@ -75,7 +75,7 @@ public function testValidCode( } /** - * @return array}> + * @return array}> */ public function providerValidCodeParse(): array { diff --git a/tests/FileManipulation/ClassMoveTest.php b/tests/FileManipulation/ClassMoveTest.php index e40e62194ab..0cee3a4b7d3 100644 --- a/tests/FileManipulation/ClassMoveTest.php +++ b/tests/FileManipulation/ClassMoveTest.php @@ -75,7 +75,7 @@ public function testValidCode( } /** - * @return array}> + * @return array}> */ public function providerValidCodeParse(): array { diff --git a/tests/FileManipulation/FileManipulationTestCase.php b/tests/FileManipulation/FileManipulationTestCase.php index 98c604cc8de..dc53981aa5c 100644 --- a/tests/FileManipulation/FileManipulationTestCase.php +++ b/tests/FileManipulation/FileManipulationTestCase.php @@ -97,7 +97,7 @@ public function testValidCode( } /** - * @return array + * @return array,safe_types:bool,allow_backwards_incompatible_changes?:bool}> */ abstract public function providerValidCodeParse(): array; } diff --git a/tests/FileManipulation/ImmutableAnnotationAdditionTest.php b/tests/FileManipulation/ImmutableAnnotationAdditionTest.php index 8c0e902a331..926cef22aad 100644 --- a/tests/FileManipulation/ImmutableAnnotationAdditionTest.php +++ b/tests/FileManipulation/ImmutableAnnotationAdditionTest.php @@ -4,9 +4,6 @@ class ImmutableAnnotationAdditionTest extends FileManipulationTestCase { - /** - * @return array - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/MethodMoveTest.php b/tests/FileManipulation/MethodMoveTest.php index ef195c582d9..1d2d3ca9b04 100644 --- a/tests/FileManipulation/MethodMoveTest.php +++ b/tests/FileManipulation/MethodMoveTest.php @@ -75,7 +75,7 @@ public function testValidCode( } /** - * @return array}> + * @return array}> */ public function providerValidCodeParse(): array { diff --git a/tests/FileManipulation/MissingPropertyTypeTest.php b/tests/FileManipulation/MissingPropertyTypeTest.php index df3414ff17b..afeb644ee88 100644 --- a/tests/FileManipulation/MissingPropertyTypeTest.php +++ b/tests/FileManipulation/MissingPropertyTypeTest.php @@ -4,9 +4,6 @@ class MissingPropertyTypeTest extends FileManipulationTestCase { - /** - * @return array,safe_types:bool,allow_backwards_incompatible_changes?:bool}> - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/MissingReturnTypeTest.php b/tests/FileManipulation/MissingReturnTypeTest.php index 9c55762a3f2..062b47aaa24 100644 --- a/tests/FileManipulation/MissingReturnTypeTest.php +++ b/tests/FileManipulation/MissingReturnTypeTest.php @@ -4,9 +4,6 @@ class MissingReturnTypeTest extends FileManipulationTestCase { - /** - * @return array,safe_types:bool,allow_backwards_incompatible_changes?:bool}> - */ public function providerValidCodeParse(): array { return [ @@ -160,7 +157,7 @@ function foo() { /** * @return string[] * - * @psalm-return array{0: \'hello\'} + * @psalm-return strict-list{\'hello\'} */ function foo() { return ["hello"]; @@ -178,7 +175,7 @@ function foo() { /** * @return string[] * - * @psalm-return array{0: \'hello\'} + * @psalm-return strict-list{\'hello\'} */ function foo(): array { return ["hello"]; @@ -196,7 +193,7 @@ function foo() { /** * @return string[] * - * @psalm-return array{a: \'goodbye\'|\'hello\', b?: \'hello again\'} + * @psalm-return strict-array{a: \'goodbye\'|\'hello\', b?: \'hello again\'} */ function foo(): array { return rand(0, 1) ? ["a" => "hello"] : ["a" => "goodbye", "b" => "hello again"]; @@ -221,7 +218,7 @@ function foo() { /** * @return int[] * - * @psalm-return array{a?: 1, b?: 2} + * @psalm-return strict-array{a?: 1, b?: 2} */ function foo(): array { if (rand(0, 1)) { @@ -258,7 +255,7 @@ function foo() { /** * @return ((int|int[])[]|int)[] * - * @psalm-return array{a: 1, b: 2, c: array{a: 1, b: 2, c: array{a: 1, b: 2, c: 3}}} + * @psalm-return strict-array{a: 1, b: 2, c: strict-array{a: 1, b: 2, c: strict-array{a: 1, b: 2, c: 3}}} */ function foo(): array { return [ @@ -296,7 +293,7 @@ function foo() { /** * @return string[] * - * @psalm-return array{a: \'goodbye\'|\'hello\', b?: \'hello again\'} + * @psalm-return strict-array{a: \'goodbye\'|\'hello\', b?: \'hello again\'} */ function foo(): array { if (rand(0, 1)) { diff --git a/tests/FileManipulation/NamespaceMoveTest.php b/tests/FileManipulation/NamespaceMoveTest.php index 129663dfbb4..4c519737cb3 100644 --- a/tests/FileManipulation/NamespaceMoveTest.php +++ b/tests/FileManipulation/NamespaceMoveTest.php @@ -75,7 +75,7 @@ public function testValidCode( } /** - * @return array}> + * @return array}> */ public function providerValidCodeParse(): array { diff --git a/tests/FileManipulation/ParamNameMismatchTest.php b/tests/FileManipulation/ParamNameMismatchTest.php index 4577dc21ee3..3dfd82d778a 100644 --- a/tests/FileManipulation/ParamNameMismatchTest.php +++ b/tests/FileManipulation/ParamNameMismatchTest.php @@ -4,9 +4,6 @@ class ParamNameMismatchTest extends FileManipulationTestCase { - /** - * @return array - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/ParamTypeManipulationTest.php b/tests/FileManipulation/ParamTypeManipulationTest.php index 86d11d20cb9..9d00e1d6971 100644 --- a/tests/FileManipulation/ParamTypeManipulationTest.php +++ b/tests/FileManipulation/ParamTypeManipulationTest.php @@ -4,9 +4,6 @@ class ParamTypeManipulationTest extends FileManipulationTestCase { - /** - * @return array - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/PropertyMoveTest.php b/tests/FileManipulation/PropertyMoveTest.php index e9e98429e85..fac9489566e 100644 --- a/tests/FileManipulation/PropertyMoveTest.php +++ b/tests/FileManipulation/PropertyMoveTest.php @@ -75,7 +75,7 @@ public function testValidCode( } /** - * @return array}> + * @return array}> */ public function providerValidCodeParse(): array { diff --git a/tests/FileManipulation/PureAnnotationAdditionTest.php b/tests/FileManipulation/PureAnnotationAdditionTest.php index af8be6c1467..3434481ad4f 100644 --- a/tests/FileManipulation/PureAnnotationAdditionTest.php +++ b/tests/FileManipulation/PureAnnotationAdditionTest.php @@ -4,9 +4,6 @@ class PureAnnotationAdditionTest extends FileManipulationTestCase { - /** - * @return array - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/RedundantCastManipulationTest.php b/tests/FileManipulation/RedundantCastManipulationTest.php index 509fdd6e5ed..fa679ce3310 100644 --- a/tests/FileManipulation/RedundantCastManipulationTest.php +++ b/tests/FileManipulation/RedundantCastManipulationTest.php @@ -4,9 +4,6 @@ class RedundantCastManipulationTest extends FileManipulationTestCase { - /** - * @return array,safe_types:bool,allow_backwards_incompatible_changes?:bool}> - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/ReturnTypeManipulationTest.php b/tests/FileManipulation/ReturnTypeManipulationTest.php index 78e37f1011f..aefcca3da8e 100644 --- a/tests/FileManipulation/ReturnTypeManipulationTest.php +++ b/tests/FileManipulation/ReturnTypeManipulationTest.php @@ -4,9 +4,6 @@ class ReturnTypeManipulationTest extends FileManipulationTestCase { - /** - * @return array,safe_types:bool,allow_backwards_incompatible_changes?:bool}> - */ public function providerValidCodeParse(): array { return [ @@ -235,7 +232,7 @@ class D { /** * @return B\C[] * - * @psalm-return array{0: B\C} + * @psalm-return strict-list{B\C} */ public function getArrayOfC(): array { return [new \A\B\C]; @@ -499,7 +496,7 @@ class A { /** * @return string[] * - * @psalm-return array{0: \'hello\'} + * @psalm-return strict-list{\'hello\'} */ public function foo(): ?array { return ["hello"]; @@ -525,7 +522,7 @@ class A { /** * @return string[] * - * @psalm-return array{0: \'hello\'} + * @psalm-return strict-list{\'hello\'} */ public function foo(): array { return ["hello"]; @@ -884,7 +881,7 @@ class container1 {} * @extends container1 */ class container2 extends container1 {} - + function ret() { /** @var container1&container2 $a */ $a = new container1; @@ -901,7 +898,7 @@ class container1 {} * @extends container1 */ class container2 extends container1 {} - + /** * @return container1&container2 * @@ -929,7 +926,7 @@ class container1 {} * @extends container1 */ class container2 extends container1 {} - + function ret() { /** @var container1&container2 $a */ $a = new container1; @@ -946,7 +943,7 @@ class container1 {} * @extends container1 */ class container2 extends container1 {} - + /** * @psalm-return container1&container2 */ diff --git a/tests/FileManipulation/ThrowsBlockAdditionTest.php b/tests/FileManipulation/ThrowsBlockAdditionTest.php index 467fe05b14b..a4a2e8389b8 100644 --- a/tests/FileManipulation/ThrowsBlockAdditionTest.php +++ b/tests/FileManipulation/ThrowsBlockAdditionTest.php @@ -4,9 +4,6 @@ class ThrowsBlockAdditionTest extends FileManipulationTestCase { - /** - * @return array - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/UndefinedVariableManipulationTest.php b/tests/FileManipulation/UndefinedVariableManipulationTest.php index 7801fc74efb..5d0e09eec0f 100644 --- a/tests/FileManipulation/UndefinedVariableManipulationTest.php +++ b/tests/FileManipulation/UndefinedVariableManipulationTest.php @@ -4,9 +4,6 @@ class UndefinedVariableManipulationTest extends FileManipulationTestCase { - /** - * @return array - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/UnnecessaryVarAnnotationManipulationTest.php b/tests/FileManipulation/UnnecessaryVarAnnotationManipulationTest.php index 953e3b9f35e..99e3c22ae10 100644 --- a/tests/FileManipulation/UnnecessaryVarAnnotationManipulationTest.php +++ b/tests/FileManipulation/UnnecessaryVarAnnotationManipulationTest.php @@ -4,9 +4,6 @@ class UnnecessaryVarAnnotationManipulationTest extends FileManipulationTestCase { - /** - * @return array - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/UnusedCodeManipulationTest.php b/tests/FileManipulation/UnusedCodeManipulationTest.php index 95bcb2db1b0..efbc2c12610 100644 --- a/tests/FileManipulation/UnusedCodeManipulationTest.php +++ b/tests/FileManipulation/UnusedCodeManipulationTest.php @@ -4,9 +4,6 @@ class UnusedCodeManipulationTest extends FileManipulationTestCase { - /** - * @return array - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileManipulation/UnusedVariableManipulationTest.php b/tests/FileManipulation/UnusedVariableManipulationTest.php index 1e73f75fe25..91636f4cbe3 100644 --- a/tests/FileManipulation/UnusedVariableManipulationTest.php +++ b/tests/FileManipulation/UnusedVariableManipulationTest.php @@ -4,9 +4,6 @@ class UnusedVariableManipulationTest extends FileManipulationTestCase { - /** - * @return array - */ public function providerValidCodeParse(): array { return [ diff --git a/tests/FileReferenceTest.php b/tests/FileReferenceTest.php index 9b37cabfab4..22ac4b852ca 100644 --- a/tests/FileReferenceTest.php +++ b/tests/FileReferenceTest.php @@ -125,7 +125,7 @@ public function testReferencedMethods( } /** - * @return array}> + * @return array}> */ public function providerReferenceLocations(): array { @@ -152,7 +152,7 @@ public function foo(): void {} } /** - * @return array>, * 2: array>, @@ -311,8 +311,6 @@ public function foo() : void { [], [], [], - [], - [], ], 'staticPropertyRefs' => [ ' $start_files * @param array $end_files - * @param array $error_levels + * @param array $ignored_issues * */ public function testValidInclude( @@ -57,7 +57,7 @@ public function testValidInclude( array $end_files, array $initial_analyzed_methods, array $unaffected_analyzed_methods, - array $error_levels = [] + array $ignored_issues = [] ): void { $test_name = $this->getTestName(); if (strpos($test_name, 'SKIPPED-') !== false) { @@ -71,7 +71,7 @@ public function testValidInclude( $config = $codebase->config; $config->throw_exception = false; - foreach ($error_levels as $error_type => $error_level) { + foreach ($ignored_issues as $error_type => $error_level) { $config->setCustomErrorLevel($error_type, $error_level); } @@ -108,7 +108,7 @@ public function testValidInclude( } /** - * @return array,end_files:array,initial_analyzed_methods:array>,unaffected_analyzed_methods:array>,4?:array}> + * @return array,end_files:array,initial_analyzed_methods:array>,unaffected_analyzed_methods:array>,0?:array}> */ public function providerTestValidUpdates(): array { diff --git a/tests/FileUpdates/ErrorAfterUpdateTest.php b/tests/FileUpdates/ErrorAfterUpdateTest.php index c9805f5898b..5e68c58c14f 100644 --- a/tests/FileUpdates/ErrorAfterUpdateTest.php +++ b/tests/FileUpdates/ErrorAfterUpdateTest.php @@ -50,13 +50,13 @@ public function setUp(): void * @dataProvider providerTestInvalidUpdates * * @param array> $file_stages - * @param array $error_levels + * @param array $ignored_issues * */ public function testErrorAfterUpdate( array $file_stages, string $error_message, - array $error_levels = [] + array $ignored_issues = [] ): void { $this->project_analyzer->getCodebase()->diff_methods = true; $this->project_analyzer->getCodebase()->reportUnusedCode(); @@ -65,7 +65,7 @@ public function testErrorAfterUpdate( $config = $codebase->config; - foreach ($error_levels as $error_type => $error_level) { + foreach ($ignored_issues as $error_type => $error_level) { $config->setCustomErrorLevel($error_type, $error_level); } @@ -102,7 +102,7 @@ public function testErrorAfterUpdate( } /** - * @return array>,error_message:string}> + * @return array>,error_message:string}> */ public function providerTestInvalidUpdates(): array { diff --git a/tests/FileUpdates/ErrorFixTest.php b/tests/FileUpdates/ErrorFixTest.php index 164fef5f3cd..c435ec3251d 100644 --- a/tests/FileUpdates/ErrorFixTest.php +++ b/tests/FileUpdates/ErrorFixTest.php @@ -51,13 +51,13 @@ public function setUp(): void * * @param array> $files * @param array $error_counts - * @param array $error_levels + * @param array $ignored_issues * */ public function testErrorFix( array $files, array $error_counts, - array $error_levels = [] + array $ignored_issues = [] ): void { $this->project_analyzer->getCodebase()->diff_methods = true; @@ -65,7 +65,7 @@ public function testErrorFix( $config = $codebase->config; - foreach ($error_levels as $error_type => $error_level) { + foreach ($ignored_issues as $error_type => $error_level) { $config->setCustomErrorLevel($error_type, $error_level); } @@ -104,7 +104,7 @@ public function testErrorFix( } /** - * @return array>,error_counts:array,ignored_issues?:array}> + * @return array>,error_counts:array,ignored_issues?:array}> */ public function providerTestErrorFix(): array { @@ -193,7 +193,7 @@ public function bar() : int { ], ], 'error_counts' => [2, 1, 0], - [ + 'ignored_issues' => [ 'MissingReturnType' => Config::REPORT_INFO, ], ], diff --git a/tests/FileUpdates/TemporaryUpdateTest.php b/tests/FileUpdates/TemporaryUpdateTest.php index 95147a9f569..0231c337e6c 100644 --- a/tests/FileUpdates/TemporaryUpdateTest.php +++ b/tests/FileUpdates/TemporaryUpdateTest.php @@ -54,13 +54,13 @@ public function setUp(): void * * @param array> $file_stages * @param array> $error_positions - * @param array $error_levels + * @param array $ignored_issues * */ public function testErrorFix( array $file_stages, array $error_positions, - array $error_levels = [], + array $ignored_issues = [], bool $test_save = true, bool $check_unused_code = false ): void { @@ -73,7 +73,7 @@ public function testErrorFix( $config = $codebase->config; - foreach ($error_levels as $error_type => $error_level) { + foreach ($ignored_issues as $error_type => $error_level) { $config->setCustomErrorLevel($error_type, $error_level); } @@ -162,7 +162,7 @@ public function testErrorFix( } /** - * @return array>,error_positions:array>, ignored_issues?:array, test_save?:bool}> + * @return array>,error_positions:array>, ignored_issues?:array, test_save?:bool, check_unused_code?: bool}> */ public function providerTestErrorFix(): array { @@ -310,7 +310,7 @@ public function bar() : int { ], ], 'error_positions' => [[373], [374], [375]], - [ + 'ignored_issues' => [ 'MixedAssignment' => Config::REPORT_INFO, ], ], @@ -360,7 +360,7 @@ public function bar() : int { ], ], 'error_positions' => [[196, 144, 339, 290], [345, 296], []], - [ + 'ignored_issues' => [ 'MissingReturnType' => Config::REPORT_INFO, ], ], @@ -404,7 +404,7 @@ public function bar() : string { ], ], 'error_positions' => [[333], []], - [ + 'ignored_issues' => [ 'InvalidDocblock' => Config::REPORT_INFO, ], ], @@ -450,7 +450,7 @@ public function foo() : int { ], ], 'error_positions' => [[136, 273], [279], [193, 144]], - [ + 'ignored_issues' => [ 'MissingReturnType' => Config::REPORT_INFO, ], ], @@ -486,10 +486,10 @@ public function bar() { ], ], 'error_positions' => [[136, 273], [144, 136, 275]], - [ + 'ignored_issues' => [ 'MissingReturnType' => Config::REPORT_INFO, ], - false, + 'test_save' => false, ], 'noChangeJustWeirdDocblocks' => [ [ @@ -1728,9 +1728,9 @@ class B {}', ], ], 'error_positions' => [[84], [84]], - [], - false, - true + 'ignored_issues' => [], + 'test_save' => false, + 'check_unused_code' => true ], 'stillUnusedMethod' => [ [ @@ -1765,9 +1765,9 @@ public function bar() : void {} ], ], 'error_positions' => [[201], [234]], - [], - false, - true + 'ignored_issues' => [], + 'test_save' => false, + 'check_unused_code' => true ], 'usedMethodWithNoAffectedConstantChanges' => [ [ @@ -1824,9 +1824,9 @@ public function doFoo() : void { ], ], 'error_positions' => [[], []], - [], - false, - true + 'ignored_issues' => [], + 'test_save' => false, + 'check_unused_code' => true ], 'syntaxErrorFixed' => [ [ diff --git a/tests/ForbiddenCodeTest.php b/tests/ForbiddenCodeTest.php index 2f8ea3080ec..aa57a589685 100644 --- a/tests/ForbiddenCodeTest.php +++ b/tests/ForbiddenCodeTest.php @@ -20,7 +20,7 @@ class ForbiddenCodeTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -54,7 +54,7 @@ public function providerInvalidCodeParse(): iterable } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { diff --git a/tests/FunctionCallTest.php b/tests/FunctionCallTest.php index 9b4f21793c5..c7288f72e95 100644 --- a/tests/FunctionCallTest.php +++ b/tests/FunctionCallTest.php @@ -15,7 +15,7 @@ class FunctionCallTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -272,7 +272,7 @@ function a($b): string 'objectLikeKeyChecksAgainstTKeyedArray' => [ 'code' => ' [ - '$components' => 'array{fragment?: string, host?: string, pass?: string, path?: string, port?: int, query?: string, scheme?: string, user?: string}|false', + '$components' => 'false|strict-array{fragment?: string, host?: string, pass?: string, path?: string, port?: int, query?: string, scheme?: string, user?: string}', '$scheme' => 'false|null|string', '$host' => 'false|null|string', '$port' => 'false|int|null', @@ -693,9 +693,9 @@ function bag(string $s) : string { $b = parse_url($url, -42); $c = parse_url($url, $component);', 'assertions' => [ - '$a' => 'array{fragment?: string, host?: string, pass?: string, path?: string, port?: int, query?: string, scheme?: string, user?: string}|false', - '$b' => 'array{fragment?: string, host?: string, pass?: string, path?: string, port?: int, query?: string, scheme?: string, user?: string}|false', - '$c' => 'array{fragment?: string, host?: string, pass?: string, path?: string, port?: int, query?: string, scheme?: string, user?: string}|false', + '$a' => 'false|strict-array{fragment?: string, host?: string, pass?: string, path?: string, port?: int, query?: string, scheme?: string, user?: string}', + '$b' => 'false|strict-array{fragment?: string, host?: string, pass?: string, path?: string, port?: int, query?: string, scheme?: string, user?: string}', + '$c' => 'false|strict-array{fragment?: string, host?: string, pass?: string, path?: string, port?: int, query?: string, scheme?: string, user?: string}', ], ], 'triggerUserError' => [ @@ -1066,9 +1066,9 @@ class Props { $d = hrtime(false);', 'assertions' => [ '$a' => 'int', - '$b' => 'array{int, int}', - '$c' => 'array{int, int}|int', - '$d' => 'array{int, int}', + '$b' => 'strict-list{int, int}', + '$c' => 'int|strict-list{int, int}', + '$d' => 'strict-list{int, int}', ], ], 'hrtimeCanBeFloat' => [ @@ -1202,7 +1202,7 @@ function example($x) : int { 'code' => ' [ '$r===' => '0|1|false', - '$matches===' => 'array}>', + '$matches===' => 'array}>', ], ], 'pregMatchWithFlagUnmatchedAsNull' => [ @@ -1383,7 +1383,7 @@ function takesInt(int $i) : void {} $r = preg_match("{foo}", "foo", $matches, PREG_OFFSET_CAPTURE | PREG_UNMATCHED_AS_NULL);', 'assertions' => [ '$r===' => '0|1|false', - '$matches===' => 'array}>', + '$matches===' => 'array}>', ], ], 'pregReplaceCallback' => [ @@ -1413,7 +1413,7 @@ function(array $ids): array { };', 'assertions' => [], 'ignored_issues' => [], - '7.4' + 'php_version' => '7.4' ], 'compactDefinedVariable' => [ 'code' => ' [], - 'error_levels' => ['MixedMethodCall'], + 'ignored_issues' => ['MixedMethodCall'], 'php_version' => '8.1', ], 'refineWithEnumExists' => [ @@ -1854,7 +1854,7 @@ function foo(string $s) : void { } }', 'assertions' => [], - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '8.1', ], 'refineWithClassExistsOrEnumExists' => [ @@ -1877,7 +1877,7 @@ function baz(string $s) : void { } }', 'assertions' => [], - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '8.1', ], 'trimSavesLowercaseAttribute' => [ @@ -1911,7 +1911,7 @@ function baz(string $s) : void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -2081,7 +2081,7 @@ function a($b): int 'objectLikeKeyChecksAgainstDifferentTKeyedArray' => [ 'code' => ' 'ArgumentTypeCoercion', ], + 'array_is_list_literal_array' => [ + 'code' => ' 0, 0 => 1]; + assert(array_is_list($list));', + 'error_message' => 'TypeDoesNotContainType', + 'ignored_issues' => [], + 'php_version' => '8.1', + ] ]; } diff --git a/tests/GeneratorTest.php b/tests/GeneratorTest.php index 5a1aab47a86..1b764a51d8e 100644 --- a/tests/GeneratorTest.php +++ b/tests/GeneratorTest.php @@ -11,7 +11,7 @@ class GeneratorTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -291,14 +291,14 @@ function generator(): Generator $_a = function() { return [yield "a"]; }; ', 'assertions' => [ - '$_a' => 'pure-Closure():Generator', + '$_a' => 'pure-Closure():Generator', ] ], ]; } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/IfThisIsTest.php b/tests/IfThisIsTest.php index 248fe495583..9cc097faf91 100644 --- a/tests/IfThisIsTest.php +++ b/tests/IfThisIsTest.php @@ -11,7 +11,7 @@ class IfThisIsTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -290,7 +290,7 @@ public function compact(): ArrayList } /** - * @return array + * @return array */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ImmutableAnnotationTest.php b/tests/ImmutableAnnotationTest.php index 6907f517b66..e86089b0009 100644 --- a/tests/ImmutableAnnotationTest.php +++ b/tests/ImmutableAnnotationTest.php @@ -11,7 +11,7 @@ class ImmutableAnnotationTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -216,7 +216,7 @@ public function __serialize(): array { return ["data" => $this->data]; } - /** @param array{data: string} $data */ + /** @param strict-array{data: string} $data */ public function __unserialize(array $data): void { $this->data = $data["data"]; } @@ -551,7 +551,7 @@ function getDataItem(string $key) { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/IncludeTest.php b/tests/IncludeTest.php index 3f13695fd26..bbd895454fa 100644 --- a/tests/IncludeTest.php +++ b/tests/IncludeTest.php @@ -20,14 +20,14 @@ class IncludeTest extends TestCase * @param array $files_to_check * @param array $files * @param bool $hoist_constants - * @param array $error_levels + * @param list $ignored_issues * */ public function testValidInclude( array $files, array $files_to_check, $hoist_constants = false, - array $error_levels = [] + array $ignored_issues = [] ): void { $codebase = $this->project_analyzer->getCodebase(); @@ -43,7 +43,7 @@ public function testValidInclude( $config = $codebase->config; $config->skip_checks_on_unresolvable_includes = true; - foreach ($error_levels as $error_level) { + foreach ($ignored_issues as $error_level) { $config->setCustomErrorLevel($error_level, Config::REPORT_SUPPRESS); } @@ -99,7 +99,7 @@ public function testInvalidInclude( } /** - * @return array,files_to_check:array}> + * @return array,files_to_check:array,hoist_constants?:bool,ignored_issues?:list}> */ public function providerTestValidIncludes(): array { @@ -628,7 +628,7 @@ class Two {}', } /** - * @return array,files_to_check:array,error_message:string}> + * @return array,files_to_check:array,error_message:string}> */ public function providerTestInvalidIncludes(): array { diff --git a/tests/IntRangeTest.php b/tests/IntRangeTest.php index d4f8c78bf05..0e43208f600 100644 --- a/tests/IntRangeTest.php +++ b/tests/IntRangeTest.php @@ -23,7 +23,7 @@ public function testCombineIntRangeDoesntAffectOriginal(): void } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -489,7 +489,7 @@ function a(): array { /** @return array */ - function getArray(): array{ + function getArray(): array { return []; }' ], @@ -861,7 +861,7 @@ function bar(int $_a, int $_b): void {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/InterfaceTest.php b/tests/InterfaceTest.php index 106219b762e..01b6af37641 100644 --- a/tests/InterfaceTest.php +++ b/tests/InterfaceTest.php @@ -13,7 +13,7 @@ class InterfaceTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -743,7 +743,7 @@ function takesAorB(SomeClass|SomeInterface $some): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Internal/CallMapTest.php b/tests/Internal/CallMapTest.php index 162ea0d4ed8..d5d2a6d4ecf 100644 --- a/tests/Internal/CallMapTest.php +++ b/tests/Internal/CallMapTest.php @@ -61,9 +61,9 @@ public function testMainCallmapFileContainsACallmap(string $callMapFilePath): ar /** * @depends testDictionaryPathMustBeAReadableDirectory - * @return array>, - * changed: array, * new: array * }>, @@ -90,9 +90,9 @@ public function testDeltaFilesContainAddedChangedAndRemovedSections(): array } /** - * @var array{ + * @var strict-array{ * added: array>, - * changed: array, * new: array * }>, @@ -133,9 +133,9 @@ public function testDeltaFilesContainAddedChangedAndRemovedSections(): array * @depends testMainCallmapFileContainsACallmap * @depends testDeltaFilesContainAddedChangedAndRemovedSections * @param array> $mainCallMap - * @param array>, - * changed: array, * new: array * }>, @@ -166,9 +166,9 @@ public function testCallmapKeysAreStringsAndValuesAreSignatures(array $mainCallM * @depends testDeltaFilesContainAddedChangedAndRemovedSections * @depends testCallmapKeysAreStringsAndValuesAreSignatures * @param array> $mainCallMap - * @param array>, - * changed: array, * new: array * }>, @@ -208,9 +208,9 @@ public function testSignatureKeysAreZeroOrStringAndValuesAreTypes(array $mainCal * @depends testDeltaFilesContainAddedChangedAndRemovedSections * @depends testSignatureKeysAreZeroOrStringAndValuesAreTypes * @param array> $mainCallMap - * @param array>, - * changed: array, * new: array * }>, @@ -252,9 +252,9 @@ public function testTypesAreParsable(array $mainCallMap, array $deltaFiles): voi /** * @depends testDeltaFilesContainAddedChangedAndRemovedSections * @depends testCallmapKeysAreStringsAndValuesAreSignatures - * @param array>, - * changed: array, * new: array * }>, @@ -296,9 +296,9 @@ public function testChangedAndRemovedFunctionsMustExist(array $deltaFiles): arra * @depends testDeltaFilesContainAddedChangedAndRemovedSections * @depends testCallmapKeysAreStringsAndValuesAreSignatures * @depends testChangedAndRemovedFunctionsMustExist - * @param array>, - * changed: array, * new: array * }>, @@ -330,9 +330,9 @@ public function testExistingFunctionsCanNotBeAdded(array $deltaFiles): array /** * @depends testDeltaFilesContainAddedChangedAndRemovedSections * @depends testCallmapKeysAreStringsAndValuesAreSignatures - * @param array>, - * changed: array, * new: array * }>, @@ -445,9 +445,9 @@ public function testMainCallmapSignaturesMustMatchMostRecentIncomingSignatures(a /** * @depends testDeltaFilesContainAddedChangedAndRemovedSections * @depends testSignatureKeysAreZeroOrStringAndValuesAreTypes - * @param array>, - * changed: array, * new: array * }>, diff --git a/tests/Internal/CliUtilsTest.php b/tests/Internal/CliUtilsTest.php index 928eb0152f1..1630d6ec9d0 100644 --- a/tests/Internal/CliUtilsTest.php +++ b/tests/Internal/CliUtilsTest.php @@ -28,7 +28,7 @@ protected function tearDown(): void $argv = $this->argv; } - /** @return iterable,list}> */ + /** @return iterable,list}> */ public function provideGetArguments(): iterable { $psalter = __DIR__ . '/../../psalter'; @@ -61,7 +61,7 @@ public function testGetArgumentsWillReturnExpectedValue(array $expected, array $ self::assertEquals($expected, $result); } - /** @return iterable|null,list,fpaths?:list}> */ + /** @return iterable|null, 1: list, 2?: list}> */ public function provideGetPathsToCheck(): iterable { $psalm = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'psalm'; diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 6181f6611ec..0992c3d5d1c 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -549,7 +549,7 @@ public function testGetcallmapReturnsAValidCallmap(): void /** * - * @return iterable}> + * @return iterable}> */ public function callMapEntryProvider(): iterable { @@ -685,15 +685,14 @@ private function assertEntryParameters(ReflectionFunction $function, array $entr { /** * Parse the parameter names from the map. - * @var array + * @var array */ $normalizedEntries = []; foreach ($entryParameters as $key => $entry) { $normalizedKey = $key; /** - * - * @var array{byRef: bool, refMode: 'rw'|'w', variadic: bool, optional: bool, type: string} $normalizedEntry + * @var strict-array{byRef: bool, refMode: 'rw'|'w'|'r', variadic: bool, optional: bool, type: string} $normalizedEntry */ $normalizedEntry = [ 'variadic' => false, @@ -715,6 +714,9 @@ private function assertEntryParameters(ReflectionFunction $function, array $entr if ($normalizedEntry['byRef']) { $parts = explode('_', $normalizedKey, 2); if (count($parts) === 2) { + if (!($parts[0] === 'rw' || $parts[0] === 'w' || $parts[0] === 'r')) { + throw new InvalidArgumentException('Invalid refMode: '.$parts[0]); + } $normalizedEntry['refMode'] = $parts[0]; $normalizedKey = $parts[1]; } else { @@ -739,7 +741,7 @@ private function assertEntryParameters(ReflectionFunction $function, array $entr /** * - * @param array{byRef: bool, refMode: 'rw'|'w', variadic: bool, optional: bool, type: string} $normalizedEntry + * @param strict-array{byRef: bool, name?: string, refMode: 'rw'|'w'|'r', variadic: bool, optional: bool, type: string} $normalizedEntry */ private function assertParameter(array $normalizedEntry, ReflectionParameter $param): void { diff --git a/tests/Internal/Provider/FakeFileReferenceCacheProvider.php b/tests/Internal/Provider/FakeFileReferenceCacheProvider.php index 8e3c17ef2e6..9fb08f93020 100644 --- a/tests/Internal/Provider/FakeFileReferenceCacheProvider.php +++ b/tests/Internal/Provider/FakeFileReferenceCacheProvider.php @@ -65,10 +65,10 @@ class FakeFileReferenceCacheProvider extends FileReferenceCacheProvider /** * @var array< * string, - * array{ - * 0: array, - * 1: array, - * 2: array + * strict-array{ + * 0: array, + * 1: array, + * 2: array * } * > */ @@ -258,10 +258,10 @@ public function setAnalyzedMethodCache(array $analyzed_methods): void /** * @return array< * string, - * array{ - * 0: array, - * 1: array, - * 2: array + * strict-array{ + * 0: array, + * 1: array, + * 2: array * } * > */ @@ -273,10 +273,10 @@ public function getFileMapCache(): array /** * @param array< * string, - * array{ - * 0: array, - * 1: array, - * 2: array + * strict-array{ + * 0: array, + * 1: array, + * 2: array * } * > $file_maps */ @@ -286,7 +286,7 @@ public function setFileMapCache(array $file_maps): void } /** - * @param array $mixed_counts + * @param array $mixed_counts */ public function setTypeCoverage(array $mixed_counts): void { diff --git a/tests/InternalAnnotationTest.php b/tests/InternalAnnotationTest.php index 38f6dd5df8a..6e2a8684911 100644 --- a/tests/InternalAnnotationTest.php +++ b/tests/InternalAnnotationTest.php @@ -11,7 +11,7 @@ class InternalAnnotationTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -613,7 +613,7 @@ public function baz(): void } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/IssueSuppressionTest.php b/tests/IssueSuppressionTest.php index 8a08db45a56..2c4847b9640 100644 --- a/tests/IssueSuppressionTest.php +++ b/tests/IssueSuppressionTest.php @@ -251,7 +251,7 @@ class Foo { } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -410,7 +410,7 @@ public function foobar(): string } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/JsonOutputTest.php b/tests/JsonOutputTest.php index 1ab0c0457d4..5613b2bd777 100644 --- a/tests/JsonOutputTest.php +++ b/tests/JsonOutputTest.php @@ -68,7 +68,7 @@ public function testJsonOutputErrors( } /** - * @return array + * @return array */ public function providerTestJsonOutputErrors(): array { diff --git a/tests/KeyOfArrayTest.php b/tests/KeyOfArrayTest.php index 5fd4a560488..cf296db2e45 100644 --- a/tests/KeyOfArrayTest.php +++ b/tests/KeyOfArrayTest.php @@ -13,7 +13,7 @@ class KeyOfArrayTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -102,7 +102,7 @@ function getKey(bool $asFloat) { 'keyOfUnionListAndKeyedArray' => [ 'code' => '|array{a: int, b: int}> + * @return key-of|strict-array{a: int, b: int}> */ function getKey(bool $asInt) { if ($asInt) { @@ -155,7 +155,7 @@ function returnPropertyOfA() { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -217,7 +217,7 @@ function getKey(bool $asFloat) { 'noLiteralCAllowedInKeyOfUnionListAndKeyedArray' => [ 'code' => '|array{a: int, b: int}> + * @return key-of|strict-array{a: int, b: int}> */ function getKey() { return "c"; diff --git a/tests/LanguageServer/CompletionTest.php b/tests/LanguageServer/CompletionTest.php index ce44833a224..ddec01af2f9 100644 --- a/tests/LanguageServer/CompletionTest.php +++ b/tests/LanguageServer/CompletionTest.php @@ -1357,7 +1357,7 @@ public function testCompletionOnArrayKey(): void $completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(2, 26)); $this->assertSame( [ - 'array{bar: 2, foo: 1}', + 'strict-array{bar: 2, foo: 1}', '[', 86, ], diff --git a/tests/LanguageServer/SymbolLookupTest.php b/tests/LanguageServer/SymbolLookupTest.php index be928a96dc2..e6f89d96100 100644 --- a/tests/LanguageServer/SymbolLookupTest.php +++ b/tests/LanguageServer/SymbolLookupTest.php @@ -487,7 +487,7 @@ class A { } /** - * @return array + * @return array */ public function providerGetSignatureHelp(): array { diff --git a/tests/ListTest.php b/tests/ListTest.php index 80edb1efcd6..e371df5d778 100644 --- a/tests/ListTest.php +++ b/tests/ListTest.php @@ -13,7 +13,7 @@ class ListTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -156,7 +156,7 @@ function foo(array $vars): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Loop/DoTest.php b/tests/Loop/DoTest.php index 45f6056ce12..cf7f0dc9573 100644 --- a/tests/Loop/DoTest.php +++ b/tests/Loop/DoTest.php @@ -14,7 +14,7 @@ class DoTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -366,7 +366,7 @@ function nextEvent(bool $c): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Loop/ForTest.php b/tests/Loop/ForTest.php index dfc9b605a83..b1c3592f051 100644 --- a/tests/Loop/ForTest.php +++ b/tests/Loop/ForTest.php @@ -14,7 +14,7 @@ class ForTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -50,7 +50,7 @@ function test(): int { break; } }', - 'assignments' => [ + 'assertions' => [ '$a' => 'bool', ], ], @@ -142,7 +142,7 @@ function cartesianProduct(array $arr) : void { 'noCrashOnLongThing' => [ 'code' => ' $data + * @param list $data */ function makeData(array $data) : array { while (rand(0, 1)) { @@ -184,7 +184,7 @@ function h() { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Loop/ForeachTest.php b/tests/Loop/ForeachTest.php index 99c93c2d5d5..d7475d10ea8 100644 --- a/tests/Loop/ForeachTest.php +++ b/tests/Loop/ForeachTest.php @@ -14,7 +14,7 @@ class ForeachTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -317,7 +317,7 @@ function get_merged_dict(array $args) { $tag = null; foreach (["a", "b", "c"] as $tag) { }', - 'assignments' => [ + 'assertions' => [ '$tag' => 'string', ], ], @@ -331,7 +331,7 @@ function get_merged_dict(array $args) { $tag = null; } }', - 'assignments' => [ + 'assertions' => [ '$tag' => 'null', ], ], @@ -350,7 +350,7 @@ function get_merged_dict(array $args) { break; } }', - 'assignments' => [ + 'assertions' => [ '$tag' => 'null', ], ], @@ -364,7 +364,7 @@ function get_merged_dict(array $args) { break; } }', - 'assignments' => [ + 'assertions' => [ '$tag' => 'null|string', ], ], @@ -378,7 +378,7 @@ function get_merged_dict(array $args) { $tag = null; } }', - 'assignments' => [ + 'assertions' => [ '$tag' => 'null|string', ], ], @@ -392,7 +392,7 @@ function get_merged_dict(array $args) { break; } }', - 'assignments' => [ + 'assertions' => [ '$tag' => 'int|null|string', ], ], @@ -407,7 +407,7 @@ function get_merged_dict(array $args) { break; } }', - 'assignments' => [ + 'assertions' => [ '$tag' => 'null', ], ], @@ -424,7 +424,7 @@ function getStrings(): array { $a = $s; } }', - 'assignments' => [ + 'assertions' => [ '$a' => 'mixed', ], 'ignored_issues' => [ @@ -470,7 +470,7 @@ function getStrings(): array { continue; } }', - 'assignments' => [ + 'assertions' => [ '$a' => 'mixed', ], 'ignored_issues' => [ @@ -485,7 +485,7 @@ function getStrings(): array { $a = true; break; }', - 'assignments' => [ + 'assertions' => [ '$a' => 'bool', ], ], @@ -497,7 +497,7 @@ function getStrings(): array { $a = true; continue; }', - 'assignments' => [ + 'assertions' => [ '$a' => 'bool', ], ], @@ -514,7 +514,7 @@ function getStrings(): array { break; } }', - 'assignments' => [ + 'assertions' => [ '$a' => 'bool', ], ], @@ -528,7 +528,7 @@ function getStrings(): array { continue; } }', - 'assignments' => [ + 'assertions' => [ '$a' => 'bool', ], ], @@ -547,7 +547,7 @@ function getStrings(): array { continue; } }', - 'assignments' => [ + 'assertions' => [ '$a' => 'bool', ], ], @@ -568,7 +568,7 @@ function getStrings(): array { } } }', - 'assignments' => [ + 'assertions' => [ '$a' => 'bool', ], ], @@ -583,7 +583,7 @@ function getStrings(): array { break; }', - 'assignments' => [ + 'assertions' => [ '$a' => 'bool', ], ], @@ -619,7 +619,7 @@ function foo(array $arr): void { $r[] = $key; } }', - 'assignments' => [], + 'assertions' => [], 'ignored_issues' => [ 'MixedAssignment', 'MixedArrayAccess', ], @@ -709,7 +709,7 @@ function loopI(IteratorAggregate $coll): void loopT(new C); loopI(new C);', - 'assignments' => [], + 'assertions' => [], 'ignored_issues' => [ 'MixedAssignment', 'UndefinedThisPropertyAssignment', ], @@ -1165,7 +1165,7 @@ function foo(array $files): void } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Loop/WhileTest.php b/tests/Loop/WhileTest.php index 26f2524556c..31bdc6045c5 100644 --- a/tests/Loop/WhileTest.php +++ b/tests/Loop/WhileTest.php @@ -12,7 +12,7 @@ class WhileTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -680,7 +680,7 @@ function breakUpPathIntoParts(): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/MagicMethodAnnotationTest.php b/tests/MagicMethodAnnotationTest.php index 13ecd9a83c4..3ebf108d20c 100644 --- a/tests/MagicMethodAnnotationTest.php +++ b/tests/MagicMethodAnnotationTest.php @@ -148,7 +148,7 @@ function foo(MyException $e): int { } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -879,7 +879,7 @@ class B {} /** @var I $i */ $c = $i->foo();', - [ + 'assertions' => [ '$b' => 'A&static', '$c' => 'I&static', ] @@ -944,7 +944,7 @@ class D extends C {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/MagicPropertyTest.php b/tests/MagicPropertyTest.php index 470c3214c42..a6b9876dd27 100644 --- a/tests/MagicPropertyTest.php +++ b/tests/MagicPropertyTest.php @@ -36,7 +36,7 @@ class Child {} } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -225,7 +225,7 @@ function foo($b) : void { $a->__set("foo", $b); }', 'assertions' => [], - 'error_level' => ['MixedAssignment', 'MixedPropertyTypeCoercion'], + 'ignored_issues' => ['MixedAssignment', 'MixedPropertyTypeCoercion'], ], 'namedPropertyByVariable' => [ 'code' => 'foo = "bar"; echo $c->foo;', 'assertions' => [], - 'error_level' => ['MixedArgument'], + 'ignored_issues' => ['MixedArgument'], ], 'dontAssumeNonNullAfterPossibleMagicFetch' => [ 'code' => ' [], - 'error_level' => ['PossiblyNullPropertyFetch'], + 'ignored_issues' => ['PossiblyNullPropertyFetch'], ], 'accessInMagicGet' => [ 'code' => ' [], - 'error_level' => ['MixedReturnStatement', 'MixedInferredReturnType'], + 'ignored_issues' => ['MixedReturnStatement', 'MixedInferredReturnType'], ], 'overrideInheritedProperty' => [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/MatchTest.php b/tests/MatchTest.php index 615b7f69c95..fcb3aa9ae4d 100644 --- a/tests/MatchTest.php +++ b/tests/MatchTest.php @@ -11,7 +11,7 @@ class MatchTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -102,7 +102,7 @@ function test(array $array): array } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 8e2eaa3275d..9d7850cc7d8 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -194,7 +194,7 @@ function printInt(int $int): void { } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -1030,7 +1030,7 @@ public function create($input): BlahModel } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/MethodSignatureTest.php b/tests/MethodSignatureTest.php index 884ddc53929..5b503f2aff3 100644 --- a/tests/MethodSignatureTest.php +++ b/tests/MethodSignatureTest.php @@ -290,7 +290,7 @@ public function __soapCall( } /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -923,7 +923,7 @@ public function a(mixed $a): void {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -1565,7 +1565,7 @@ public function test() { ', 'error_message' => 'MethodSignatureMismatch', 'ignored_issues' => [], - '8.0' + 'php_version' => '8.0' ], 'noTypehintInNativeDescendant' => [ 'code' => ' 'MethodSignatureMustProvideReturnType', 'ignored_issues' => [], - '8.1' + 'php_version' => '8.1' ], ]; } diff --git a/tests/MixinAnnotationTest.php b/tests/MixinAnnotationTest.php index c89531bf2ec..b1b088d5c4c 100644 --- a/tests/MixinAnnotationTest.php +++ b/tests/MixinAnnotationTest.php @@ -11,7 +11,7 @@ class MixinAnnotationTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -601,7 +601,7 @@ class FooModel extends Model {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/NamespaceTest.php b/tests/NamespaceTest.php index ffb129fe970..e6b53727a77 100644 --- a/tests/NamespaceTest.php +++ b/tests/NamespaceTest.php @@ -11,7 +11,7 @@ class NamespaceTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -75,7 +75,7 @@ function foo() : void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/NativeIntersectionsTest.php b/tests/NativeIntersectionsTest.php index d64587fc4ae..4ffe3366278 100644 --- a/tests/NativeIntersectionsTest.php +++ b/tests/NativeIntersectionsTest.php @@ -11,7 +11,7 @@ class NativeIntersectionsTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -58,7 +58,7 @@ function test(A&B $in): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/NativeUnionsTest.php b/tests/NativeUnionsTest.php index 679c6d6bf51..cdae3105432 100644 --- a/tests/NativeUnionsTest.php +++ b/tests/NativeUnionsTest.php @@ -10,9 +10,6 @@ class NativeUnionsTest extends TestCase use InvalidCodeAnalysisTestTrait; use ValidCodeAnalysisTestTrait; - /** - * @return iterable,ignored_issues?:list,php_version?:string}> - */ public function providerValidCodeParse(): iterable { return [ @@ -31,7 +28,7 @@ public function self(): A|B } }', 'assertions' => [], - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '8.0' ], 'nativeTypeUnionAsArgument' => [ @@ -52,7 +49,7 @@ function test(A|B $in): void { test(new C()); ', 'assertions' => [], - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '8.0' ], 'unionAndNullableEquivalent' => [ @@ -62,14 +59,14 @@ function test(string|null $in): ?string { } ', 'assertions' => [], - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '8.0' ], ]; } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Php40Test.php b/tests/Php40Test.php index dd67e872453..9cc142cbe05 100644 --- a/tests/Php40Test.php +++ b/tests/Php40Test.php @@ -9,7 +9,7 @@ class Php40Test extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { diff --git a/tests/Php55Test.php b/tests/Php55Test.php index 8ed1c723a65..5458a051018 100644 --- a/tests/Php55Test.php +++ b/tests/Php55Test.php @@ -9,7 +9,7 @@ class Php55Test extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { diff --git a/tests/Php56Test.php b/tests/Php56Test.php index e7ae3022cc8..699d10da9db 100644 --- a/tests/Php56Test.php +++ b/tests/Php56Test.php @@ -9,7 +9,7 @@ class Php56Test extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { diff --git a/tests/Php70Test.php b/tests/Php70Test.php index 9c2a3021303..e735000a59a 100644 --- a/tests/Php70Test.php +++ b/tests/Php70Test.php @@ -11,7 +11,7 @@ class Php70Test extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -217,7 +217,7 @@ class B { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Php71Test.php b/tests/Php71Test.php index 4467a991e89..2ee9fa8f66c 100644 --- a/tests/Php71Test.php +++ b/tests/Php71Test.php @@ -11,7 +11,7 @@ class Php71Test extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -276,7 +276,7 @@ function create_resource($op) { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/PropertiesOfTest.php b/tests/PropertiesOfTest.php index 77d6c248d59..588ab15d1a1 100644 --- a/tests/PropertiesOfTest.php +++ b/tests/PropertiesOfTest.php @@ -11,7 +11,7 @@ class PropertiesOfTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -126,6 +126,19 @@ function returnPropertyOfA(int $visibility) { } ', ], + 'finalPropertiesOf' => [ + 'code' => ' */ + function returnPropertyOfA() { + return ["foo" => true]; + } + ', + ], 'allPropertiesOfContainsNoStatic' => [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -377,6 +390,24 @@ function returnPropertyOfA() { ', 'error_message' => 'InvalidReturnStatement' ], + 'finalPropertiesOfInexact' => [ + 'code' => ' */ + function returnPropertyOfA() { + return ["foo" => true]; + } + ', + 'error_message' => 'InvalidReturnStatement' + ], ]; } } diff --git a/tests/PropertyTypeInvarianceTest.php b/tests/PropertyTypeInvarianceTest.php index d85e0de5883..19e30a39837 100644 --- a/tests/PropertyTypeInvarianceTest.php +++ b/tests/PropertyTypeInvarianceTest.php @@ -11,7 +11,7 @@ class PropertyTypeInvarianceTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -237,9 +237,6 @@ class Baz extends Bar { ]; } - /** - * @return iterable,php_version?:string}> - */ public function providerInvalidCodeParse(): iterable { return [ @@ -336,7 +333,7 @@ class Pair } /** - * @psalm-suppress MissingTemplateParam + * @psalm-suppress MissingTemplateParam */ class FooPair extends Pair { diff --git a/tests/PropertyTypeTest.php b/tests/PropertyTypeTest.php index b1617548142..c780636c392 100644 --- a/tests/PropertyTypeTest.php +++ b/tests/PropertyTypeTest.php @@ -353,7 +353,7 @@ public function two(): bool { return $has_changes; } - + public function alter() : void { if (rand(0, 1)) { array_pop($this->a); @@ -551,7 +551,7 @@ public function getX(bool $b): int { } /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -1068,7 +1068,7 @@ class C /** * @param string[] $opts - * @psalm-param array{a:string,b:string} $opts + * @psalm-param strict-array{a:string,b:string} $opts */ public function __construct(array $opts) { @@ -1077,7 +1077,7 @@ public function __construct(array $opts) /** * @param string[] $opts - * @psalm-param array{a:string,b:string} $opts + * @psalm-param strict-array{a:string,b:string} $opts */ final public function setOptions(array $opts): void { @@ -1102,7 +1102,7 @@ final class C /** * @param string[] $opts - * @psalm-param array{a:string,b:string} $opts + * @psalm-param strict-array{a:string,b:string} $opts */ public function __construct(array $opts) { @@ -1111,7 +1111,7 @@ public function __construct(array $opts) /** * @param string[] $opts - * @psalm-param array{a:string,b:string} $opts + * @psalm-param strict-array{a:string,b:string} $opts */ public function setOptions(array $opts): void { @@ -1757,7 +1757,7 @@ public function setFoo() : void { 'code' => ' false, @@ -2072,7 +2072,7 @@ function takesString(string $_s) : void {} class Foo { private $bar; - /** @psalm-param array{key: string} $bar */ + /** @psalm-param strict-array{key: string} $bar */ public function __construct(array $bar) { $this->bar = $bar; } @@ -2680,7 +2680,7 @@ public function doBar(): ?self } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/PureAnnotationTest.php b/tests/PureAnnotationTest.php index bdf6a90d0c8..00b0f8dc5a6 100644 --- a/tests/PureAnnotationTest.php +++ b/tests/PureAnnotationTest.php @@ -11,7 +11,7 @@ class PureAnnotationTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -467,7 +467,7 @@ class Date2{ } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/PureCallableTest.php b/tests/PureCallableTest.php index bb9e551d1e7..67feb644a05 100644 --- a/tests/PureCallableTest.php +++ b/tests/PureCallableTest.php @@ -11,7 +11,7 @@ class PureCallableTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -238,7 +238,7 @@ function max_by(array $values, callable $num_func) : ?int { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ReadonlyPropertyTest.php b/tests/ReadonlyPropertyTest.php index b29548c518d..797d90b1f3e 100644 --- a/tests/ReadonlyPropertyTest.php +++ b/tests/ReadonlyPropertyTest.php @@ -13,7 +13,7 @@ class ReadonlyPropertyTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -135,7 +135,7 @@ public function __construct() { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ReferenceConstraintTest.php b/tests/ReferenceConstraintTest.php index 2cdec8413a4..840afa2dede 100644 --- a/tests/ReferenceConstraintTest.php +++ b/tests/ReferenceConstraintTest.php @@ -11,7 +11,7 @@ class ReferenceConstraintTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -164,7 +164,7 @@ function addValue(&$value) : void { 'paramOutArrayDefaultNullWithThrow' => [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ReferenceTest.php b/tests/ReferenceTest.php index 3f3262fcb65..94bda657adb 100644 --- a/tests/ReferenceTest.php +++ b/tests/ReferenceTest.php @@ -13,11 +13,22 @@ class ReferenceTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { return [ + 'referenceAssignmentToNonReferenceCountsAsUse' => [ + 'code' => ' [ + '$b===' => '2', + '$a===' => '2', + ], + ], 'updateReferencedTypes' => [ 'code' => ' [ 'code' => ' [ - '$reference===' => 'array{id: 1}', + '$reference===' => 'strict-array{id: 1}', ], ], 'multipleReferencesToArrayVariableOffsetThatChangesDueToReconciliation' => [ @@ -282,15 +293,15 @@ function func(array &$a): void $reference1["id"] = 2; ', 'assertions' => [ - '$reference1===' => 'array{id: 2}', - '$reference2===' => 'array{id: 2}', + '$reference1===' => 'strict-array{id: 2}', + '$reference2===' => 'strict-array{id: 2}', ], ], ]; } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ReturnTypeProvider/ArrayColumnTest.php b/tests/ReturnTypeProvider/ArrayColumnTest.php index ea6a8d604f1..684e9564bb7 100644 --- a/tests/ReturnTypeProvider/ArrayColumnTest.php +++ b/tests/ReturnTypeProvider/ArrayColumnTest.php @@ -55,7 +55,7 @@ public function f(): array { yield 'arrayColumnWithShapes' => [ 'code' => ' */ function f(array $shape): array { @@ -145,7 +145,7 @@ public function providerInvalidCodeParse(): iterable { yield 'arrayColumnWithArrayAndColumnNameNull' => [ 'code' => ' $arrays */ + /** @var list $arrays */ $arrays = []; foreach (array_column($arrays, null, "name") as $array) { $array["instance"]->foo(); diff --git a/tests/ReturnTypeProvider/ExceptionCodeTest.php b/tests/ReturnTypeProvider/ExceptionCodeTest.php index 6ebb68196e3..ec1e389604d 100644 --- a/tests/ReturnTypeProvider/ExceptionCodeTest.php +++ b/tests/ReturnTypeProvider/ExceptionCodeTest.php @@ -10,7 +10,7 @@ class ExceptionCodeTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { diff --git a/tests/ReturnTypeProvider/GetObjectVarsTest.php b/tests/ReturnTypeProvider/GetObjectVarsTest.php index 0edbfe083e4..1fe26f434da 100644 --- a/tests/ReturnTypeProvider/GetObjectVarsTest.php +++ b/tests/ReturnTypeProvider/GetObjectVarsTest.php @@ -22,6 +22,17 @@ class C { 'assertions' => ['$ret' => 'array{prop: string}'], ]; + yield 'returnsSealedArrayForFinalClass' => [ + 'code' => ' ['$ret' => 'strict-array{prop: string}'], + ]; + yield 'omitsPrivateAndProtectedPropertiesWhenCalledOutsideOfClassScope' => [ 'code' => ' ' 1]);', 'assertions' => ['$ret' => 'array{a: int}'], ]; + + yield 'templatedProperties' => [ + 'code' => ' [ + '$a===' => "strict-array{t: 'test'}" + ] + ]; + + yield 'SKIPPED-dynamicProperties' => [ + 'code' => 'b = "test"; + $test = get_object_vars($a);', + 'assertions' => [ + '$test===' => "array{t: 'test'}" + ] + ]; + + yield 'SKIPPED-dynamicProperties82' => [ + 'code' => 'b = "test"; + $test = get_object_vars($a);', + 'assertions' => [ + '$test===' => "array{t: 'test'}" + ], + 'php_version' => '8.2' + ]; + + yield 'SKIPPED-noDynamicProperties82' => [ + 'code' => ' [ + '$test===' => "strict-array{t: 'test'}" + ], + 'php_version' => '8.2' + ]; } } diff --git a/tests/ReturnTypeTest.php b/tests/ReturnTypeTest.php index 2c01eed210c..ff5af60a0e8 100644 --- a/tests/ReturnTypeTest.php +++ b/tests/ReturnTypeTest.php @@ -13,7 +13,7 @@ class ReturnTypeTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -498,14 +498,14 @@ function foo4(): iterable { ], 'objectLikeArrayOptionalKeyReturn' => [ 'code' => ' 1, "b" => 2] : ["a" => 2]; }', ], 'objectLikeArrayOptionalKeyReturnSeparateStatements' => [ 'code' => ' 1, "b" => 2]; @@ -514,24 +514,6 @@ function foo() : array { return ["a" => 2]; }', ], - 'badlyCasedReturnType' => [ - 'code' => ' */ - public static function test() : array { - return [new Example()]; - } - - /** @return example */ - public static function instance() { - return new Example(); - } - }', - 'assertions' => [], - 'ignored_issues' => ['InvalidClass'], - ], 'arrayReturnTypeWithExplicitKeyType' => [ 'code' => ' */ @@ -723,7 +705,7 @@ class Foo { ]; /** - * @return iterable}> + * @return iterable}> */ public function foo() { return [ @@ -870,7 +852,7 @@ function returnsInt(): int { 'infersObjectShapeOfCastArray' => [ 'code' => ' 1]; @@ -1140,7 +1122,7 @@ function foo(array $x): array { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -1367,7 +1349,7 @@ function foo(array $a) { ], 'objectLikeArrayOptionalKeyWithNonOptionalReturn' => [ 'code' => ' 1, "b" => 2] : ["a" => 2]; }', @@ -1533,6 +1515,9 @@ function map(): callable { 'code' => ' $a + * + * This is unsealed because there is no way to mark a TArray as sealed. + * * @return array{from: bool, to: bool} */ function foo(array $a) : array { @@ -1612,6 +1597,23 @@ public function __construct() { } }', 'error_message' => 'LessSpecificImplementedReturnType', + ], + 'badlyCasedReturnType' => [ + 'code' => ' */ + public static function test() : array { + return [new Example()]; + } + + /** @return example */ + public static function instance() { + return new Example(); + } + }', + 'error_message' => 'InvalidClass', ] ]; } diff --git a/tests/StubTest.php b/tests/StubTest.php index 736943df32f..97bc8d54042 100644 --- a/tests/StubTest.php +++ b/tests/StubTest.php @@ -899,7 +899,7 @@ public function testStubFileWithExistingClassDefinition(): void $this->analyzeFile($file_path, new Context()); } - /** @return iterable */ + /** @return iterable */ public function versionDependentStubsProvider(): iterable { yield '7.0' => [ diff --git a/tests/SuperGlobalsTest.php b/tests/SuperGlobalsTest.php index 3e7d54ad572..a1780fd9897 100644 --- a/tests/SuperGlobalsTest.php +++ b/tests/SuperGlobalsTest.php @@ -9,7 +9,7 @@ class SuperGlobalsTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { diff --git a/tests/SwitchTypeTest.php b/tests/SwitchTypeTest.php index d101015a851..a1fbb3641f0 100644 --- a/tests/SwitchTypeTest.php +++ b/tests/SwitchTypeTest.php @@ -13,7 +13,7 @@ class SwitchTypeTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -1091,7 +1091,7 @@ function bar (string $name): int { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/TaintTest.php b/tests/TaintTest.php index 3058697a180..cd093ea0c64 100644 --- a/tests/TaintTest.php +++ b/tests/TaintTest.php @@ -65,7 +65,7 @@ public function testInvalidCode(string $code, string $error_message): void } /** - * @return array + * @return array */ public function providerValidCodeParse(): array { @@ -726,7 +726,7 @@ function bar(array $arr): void { } /** - * @return array + * @return array */ public function providerInvalidCodeParse(): array { @@ -2404,7 +2404,7 @@ public function multipleTaintIssuesAreDetected(string $code, array $expectedIssu } /** - * @return array}> + * @return array}> */ public function multipleTaintIssuesAreDetectedDataProvider(): array { diff --git a/tests/Template/ClassStringMapTest.php b/tests/Template/ClassStringMapTest.php index 0ec5e078a74..f3a4a47a947 100644 --- a/tests/Template/ClassStringMapTest.php +++ b/tests/Template/ClassStringMapTest.php @@ -12,7 +12,7 @@ class ClassStringMapTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -90,7 +90,7 @@ function foo(array $arr) : void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Template/ClassTemplateCovarianceTest.php b/tests/Template/ClassTemplateCovarianceTest.php index fd28d731439..494f1d4235c 100644 --- a/tests/Template/ClassTemplateCovarianceTest.php +++ b/tests/Template/ClassTemplateCovarianceTest.php @@ -12,7 +12,7 @@ class ClassTemplateCovarianceTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -487,7 +487,7 @@ public function getNested(): IChildCollection; */ interface IParentCollection { /** - * @return IParentCollection + * @return IParentCollection */ public function getNested(): IParentCollection; } @@ -499,7 +499,7 @@ public function getNested(): IParentCollection; */ interface IChildCollection extends IParentCollection { /** - * @return IChildCollection + * @return IChildCollection */ public function getNested(): IChildCollection; }', @@ -535,7 +535,7 @@ function run(Container $container): void{} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Template/ClassTemplateExtendsTest.php b/tests/Template/ClassTemplateExtendsTest.php index ea883366ca3..7d444a987d8 100644 --- a/tests/Template/ClassTemplateExtendsTest.php +++ b/tests/Template/ClassTemplateExtendsTest.php @@ -14,7 +14,7 @@ class ClassTemplateExtendsTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -1376,7 +1376,7 @@ public function add($t) : void; /** * @template T - * + * * @psalm-suppress MissingTemplateParam */ class C implements I { @@ -1417,7 +1417,7 @@ public function add($t) : void; /** * @template T2 - * + * * @psalm-suppress MissingTemplateParam */ class C implements I { @@ -1769,7 +1769,7 @@ public function __set(string $property, $value) { } } - /** @extends DataBag */ + /** @extends DataBag */ class FooBag extends DataBag {} $foo = new FooBag(["a" => 5, "b" => "hello"]); @@ -1794,7 +1794,7 @@ class FooBag extends DataBag {} abstract class Foo {} /** - * @template-extends Foo + * @template-extends Foo * * @internal */ @@ -1843,7 +1843,7 @@ abstract public function getIdProperty() : string; } /** - * @template-extends Foo + * @template-extends Foo */ class FooChild extends Foo { public function getIdProperty() : string { @@ -2250,7 +2250,7 @@ public function getAnother() { /** * @template TT - * + * * @extends Container */ class MyContainer extends Container {} @@ -4052,14 +4052,14 @@ function c(): Promise { */ class a { } - + /** * @template TT1 * @template TT2 * @extends a */ class b extends a {} - + /** @return Generator, mixed, "test2"> */ function bb(): \Generator { /** @var b<"test1", "test2"> */ @@ -4076,12 +4076,12 @@ function bb(): \Generator { */ class a { } - + /** * @extends a<"test"> */ class b extends a {} - + /** @return Generator */ function bb(): \Generator { $b = new b; @@ -4094,7 +4094,7 @@ function bb(): \Generator { /** @psalm-yield int */ class a {} - + /** * @return Generator */ @@ -4130,7 +4130,7 @@ abstract class WriteModel implements WriteModelInterface /** * @extends WriteModelInterface< - * array{ + * strict-array{ * ulid: string, * senderPersonId: int * } @@ -4142,7 +4142,7 @@ interface EmailWriteModelInterface extends WriteModelInterface /** * @psalm-immutable - * @extends WriteModel @@ -4637,7 +4637,7 @@ class Secondary {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Template/ClassTemplateTest.php b/tests/Template/ClassTemplateTest.php index 282380d4f2f..48994b85f28 100644 --- a/tests/Template/ClassTemplateTest.php +++ b/tests/Template/ClassTemplateTest.php @@ -14,7 +14,7 @@ class ClassTemplateTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -1021,7 +1021,7 @@ class AppUser extends User {} */ class Collection { /** - * @return array{0:Collection,1:Collection} + * @return strict-array{0:Collection,1:Collection} * @psalm-suppress InvalidReturnType */ public function partition() {} @@ -3378,7 +3378,7 @@ function foo(A $a) : void { 'templateIsAComplexMultilineType' => [ 'code' => ' $wm - * @return array{Throwable,int} + * @return strict-array{Throwable,int} */ function isTraverable(WeakMap $wm): array { foreach ($wm as $k => $v) { @@ -3517,7 +3517,7 @@ public function __construct($data) { } } - /** @param Container $r */ + /** @param Container $r */ function takesContainer(Container $r): void { $r->data = ["name" => "David"]; } @@ -3993,7 +3993,7 @@ function foobar(DoesNotExist $baz): void {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -4275,7 +4275,7 @@ public function __set(string $property, $value) { } } - /** @extends Row */ + /** @extends Row */ class CharacterRow extends Row {} $mario = new CharacterRow(["id" => 5, "name" => "Mario", "height" => 3.5]); diff --git a/tests/Template/ConditionalReturnTypeTest.php b/tests/Template/ConditionalReturnTypeTest.php index 726fab218d9..98f13c0a74e 100644 --- a/tests/Template/ConditionalReturnTypeTest.php +++ b/tests/Template/ConditionalReturnTypeTest.php @@ -10,7 +10,7 @@ class ConditionalReturnTypeTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { diff --git a/tests/Template/FunctionClassStringTemplateTest.php b/tests/Template/FunctionClassStringTemplateTest.php index 34edd8fc5df..46ef12118dd 100644 --- a/tests/Template/FunctionClassStringTemplateTest.php +++ b/tests/Template/FunctionClassStringTemplateTest.php @@ -12,7 +12,7 @@ class FunctionClassStringTemplateTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -780,7 +780,7 @@ function getSourceAssertType(string $class, MyObject $source): MyObject { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Template/FunctionTemplateAssertTest.php b/tests/Template/FunctionTemplateAssertTest.php index 61e9674c561..a71c2d8815e 100644 --- a/tests/Template/FunctionTemplateAssertTest.php +++ b/tests/Template/FunctionTemplateAssertTest.php @@ -12,7 +12,7 @@ class FunctionTemplateAssertTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -923,7 +923,7 @@ function acceptsArray(array $_list): void {} } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Template/FunctionTemplateTest.php b/tests/Template/FunctionTemplateTest.php index 4369152eb30..fcad3997ba1 100644 --- a/tests/Template/FunctionTemplateTest.php +++ b/tests/Template/FunctionTemplateTest.php @@ -12,7 +12,7 @@ class FunctionTemplateTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -847,8 +847,8 @@ function example_sort_by_ref(array &$arr): bool { } /** - * @param array $array - * @return list + * @param array $array + * @return list */ function example(array $array): array { example_sort_by_ref($array); @@ -1625,7 +1625,7 @@ function foo(?float $p): ?float } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -2165,7 +2165,7 @@ function foo(B $o):void {} 'preventBadArraySubtyping' => [ 'code' => ' [ 'code' => ',ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -99,7 +99,7 @@ function toListOfKeys(array $array): array { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Template/NestedTemplateTest.php b/tests/Template/NestedTemplateTest.php index 9550f054618..fceb2620d9d 100644 --- a/tests/Template/NestedTemplateTest.php +++ b/tests/Template/NestedTemplateTest.php @@ -12,7 +12,7 @@ class NestedTemplateTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -128,7 +128,7 @@ function toList(array $arr): array { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Template/PropertiesOfTemplateTest.php b/tests/Template/PropertiesOfTemplateTest.php index 3ec94ab7563..414ae7a1aae 100644 --- a/tests/Template/PropertiesOfTemplateTest.php +++ b/tests/Template/PropertiesOfTemplateTest.php @@ -12,7 +12,7 @@ class PropertiesOfTemplateTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -195,7 +195,7 @@ class A { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Template/TraitTemplateTest.php b/tests/Template/TraitTemplateTest.php index dac42305921..f867c63ff56 100644 --- a/tests/Template/TraitTemplateTest.php +++ b/tests/Template/TraitTemplateTest.php @@ -12,7 +12,7 @@ class TraitTemplateTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -389,10 +389,10 @@ protected function doNormalize($v): string 'code' => 'foo = $foo; @@ -517,7 +517,7 @@ public function get() { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Template/ValueOfTemplateTest.php b/tests/Template/ValueOfTemplateTest.php index 62d3a9d2c36..2d5247436d9 100644 --- a/tests/Template/ValueOfTemplateTest.php +++ b/tests/Template/ValueOfTemplateTest.php @@ -14,7 +14,7 @@ class ValueOfTemplateTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -75,7 +75,7 @@ function toList(array $array): array { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/ThisOutTest.php b/tests/ThisOutTest.php index 87aecf2d9f2..b3652771543 100644 --- a/tests/ThisOutTest.php +++ b/tests/ThisOutTest.php @@ -9,7 +9,7 @@ class ThisOutTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { diff --git a/tests/ToStringTest.php b/tests/ToStringTest.php index 2c14e072c33..0171b249318 100644 --- a/tests/ToStringTest.php +++ b/tests/ToStringTest.php @@ -11,7 +11,7 @@ class ToStringTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -211,7 +211,7 @@ public function __toString() { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -444,7 +444,7 @@ function f(S $s): array { interface S { public function __toString(): string; } - /** @return array{string} */ + /** @return strict-array{string} */ function f(S $s): array { return [$s]; } @@ -456,7 +456,7 @@ function f(S $s): array { interface S { public function __toString(): string; } - /** @return array{0:string} */ + /** @return strict-array{0:string} */ function f(S $s): array { return [$s]; } diff --git a/tests/TraceTest.php b/tests/TraceTest.php index b7aea4ac926..50c463885f8 100644 --- a/tests/TraceTest.php +++ b/tests/TraceTest.php @@ -9,7 +9,7 @@ class TraceTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/TraitTest.php b/tests/TraitTest.php index e747dd7ee52..a4fa27e9e3a 100644 --- a/tests/TraitTest.php +++ b/tests/TraitTest.php @@ -13,7 +13,7 @@ class TraitTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -1000,7 +1000,7 @@ trait Foo {}', } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/Traits/InvalidCodeAnalysisTestTrait.php b/tests/Traits/InvalidCodeAnalysisTestTrait.php index d139f74c9f6..4a90850d0d2 100644 --- a/tests/Traits/InvalidCodeAnalysisTestTrait.php +++ b/tests/Traits/InvalidCodeAnalysisTestTrait.php @@ -19,7 +19,7 @@ trait InvalidCodeAnalysisTestTrait { /** - * @return iterable,php_version?:string}> + * @return iterable,php_version?:string}> */ abstract public function providerInvalidCodeParse(): iterable; diff --git a/tests/Traits/ValidCodeAnalysisTestTrait.php b/tests/Traits/ValidCodeAnalysisTestTrait.php index 37a38dc5dee..8ea32851303 100644 --- a/tests/Traits/ValidCodeAnalysisTestTrait.php +++ b/tests/Traits/ValidCodeAnalysisTestTrait.php @@ -18,7 +18,7 @@ trait ValidCodeAnalysisTestTrait { /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * @return iterable,ignored_issues?:list,php_version?:string}> */ abstract public function providerValidCodeParse(): iterable; @@ -27,14 +27,14 @@ abstract public function providerValidCodeParse(): iterable; * * @param string $code * @param array $assertions - * @param list $error_levels + * @param list $ignored_issues * * @small */ public function testValidCode( $code, $assertions = [], - $error_levels = [], + $ignored_issues = [], string $php_version = '7.3' ): void { $test_name = $this->getTestName(); @@ -50,11 +50,8 @@ public function testValidCode( $this->markTestSkipped('Skipped due to a bug.'); } - foreach ($error_levels as $error_level) { - $issue_name = $error_level; - $error_level = Config::REPORT_SUPPRESS; - - Config::getInstance()->setCustomErrorLevel($issue_name, $error_level); + foreach ($ignored_issues as $issue_name) { + Config::getInstance()->setCustomErrorLevel($issue_name, Config::REPORT_SUPPRESS); } if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { diff --git a/tests/TryCatchTest.php b/tests/TryCatchTest.php index b8fa7a47e79..de74cceb8fb 100644 --- a/tests/TryCatchTest.php +++ b/tests/TryCatchTest.php @@ -11,7 +11,7 @@ class TryCatchTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -495,7 +495,7 @@ function foo() : void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/TypeAnnotationTest.php b/tests/TypeAnnotationTest.php index ffae930bdea..118125335cf 100644 --- a/tests/TypeAnnotationTest.php +++ b/tests/TypeAnnotationTest.php @@ -11,7 +11,7 @@ class TypeAnnotationTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -157,7 +157,7 @@ function() { } /** - * @psalm-type _A=array{elt:int} + * @psalm-type _A=strict-array{elt:int} * @param _A $p * @return _A */ @@ -171,7 +171,7 @@ function f($p) { 'code' => ' ' ' [ 'code' => ' [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -621,7 +621,7 @@ class A {}', * @psalm-type aType null|"a"|"b"|"c"|"d" */ - /** @psalm-return array{0:bool,1:aType} */ + /** @psalm-return strict-array{0:bool,1:aType} */ function f(): array { return [(bool)rand(0,1), rand(0,1) ? "z" : null]; }', @@ -631,7 +631,7 @@ function f(): array { 'code' => ' [ 'code' => ',ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -91,7 +91,7 @@ function expectsTraversableOrArray($_a): void } /** - * @return array}> + * @return array}> */ public function providerTestValidTypeCombination(): array { @@ -357,66 +357,66 @@ public function providerTestValidTypeCombination(): array ], ], 'combineObjectType1' => [ - 'array{a?: int, b?: string}', + 'strict-array{a?: int, b?: string}', [ - 'array{a: int}', - 'array{b: string}', + 'strict-array{a: int}', + 'strict-array{b: string}', ], ], 'combineObjectType2' => [ - 'array{a: int|string, b?: string}', + 'strict-array{a: int|string, b?: string}', [ - 'array{a: int}', - 'array{a: string,b: string}', + 'strict-array{a: int}', + 'strict-array{a: string,b: string}', ], ], 'combineObjectTypeWithIntKeyedArray' => [ "array<'a'|int, int|string>", [ - 'array{a: int}', + 'strict-array{a: int}', 'array', ], ], 'combineNestedObjectTypeWithTKeyedArrayIntKeyedArray' => [ - "array{a: array<'a'|int, int|string>}", + "strict-array{a: array<'a'|int, int|string>}", [ - 'array{a: array{a: int}}', - 'array{a: array}', + 'strict-array{a: strict-array{a: int}}', + 'strict-array{a: array}', ], ], 'combineIntKeyedObjectTypeWithNestedIntKeyedArray' => [ "array>", [ - 'array', + 'array', 'array>', ], ], 'combineNestedObjectTypeWithNestedIntKeyedArray' => [ "array<'a'|int, array<'a'|int, int|string>>", [ - 'array{a: array{a: int}}', + 'strict-array{a: strict-array{a: int}}', 'array>', ], ], 'combinePossiblyUndefinedKeys' => [ - 'array{a: bool, b?: mixed, d?: mixed}', + 'strict-array{a: bool, b?: mixed, d?: mixed}', [ - 'array{a: false, b: mixed}', - 'array{a: true, d: mixed}', - 'array{a: true, d: mixed}', + 'strict-array{a: false, b: mixed}', + 'strict-array{a: true, d: mixed}', + 'strict-array{a: true, d: mixed}', ], ], 'combinePossiblyUndefinedKeysAndString' => [ - 'array{a: string, b?: int}|string', + 'strict-array{a: string, b?: int}|string', [ - 'array{a: string, b?: int}', + 'strict-array{a: string, b?: int}', 'string', ], ], 'combineMixedArrayWithTKeyedArray' => [ 'array', [ - 'array{a: int}', + 'strict-array{a: int}', 'array', ], ], @@ -466,7 +466,7 @@ public function providerTestValidTypeCombination(): array "array<'a'|'b'|'c', 1|2|3>", [ 'array<"a"|"b"|"c", 1|2|3>', - 'array{a: 1|2, b: 2|3, c: 1|3}', + 'strict-array{a: 1|2, b: 2|3, c: 1|3}', ], ], 'combineClosures' => [ @@ -584,7 +584,7 @@ public function providerTestValidTypeCombination(): array 'combineCallableArrayAndArray' => [ 'array', [ - 'callable-array{class-string, string}', + 'callable-strict-array{class-string, string}', 'array', ], ], @@ -598,14 +598,14 @@ public function providerTestValidTypeCombination(): array 'combineTKeyedArrayAndArray' => [ 'array', [ - 'array{hello: int}', + 'strict-array{hello: int}', 'array', ], ], 'combineTKeyedArrayAndNestedArray' => [ 'array', [ - 'array{hello: array{goodbye: int}}', + 'strict-array{hello: strict-array{goodbye: int}}', 'array', ], ], @@ -624,10 +624,10 @@ public function providerTestValidTypeCombination(): array ], ], 'combineNonEmptyListWithTKeyedArrayList' => [ - 'array{0: null|string}', + 'list{null|string}', [ 'non-empty-list', - 'array{null}' + 'strict-array{null}' ], ], 'combineZeroAndPositiveInt' => [ @@ -701,7 +701,7 @@ public function providerTestValidTypeCombination(): array 'array', [ 'non-empty-array', - 'array{0?:int}', + 'strict-array{0?:int}', ] ], 'combineNonEmptyStringAndLiteral' => [ diff --git a/tests/TypeComparatorTest.php b/tests/TypeComparatorTest.php index 6833c3b868a..992f3e3f813 100644 --- a/tests/TypeComparatorTest.php +++ b/tests/TypeComparatorTest.php @@ -54,7 +54,7 @@ public function testTypeAcceptsItself(string $type_string): void } /** - * @return array + * @return array */ public function getAllBasicTypes(): array { @@ -78,8 +78,15 @@ public function getAllBasicTypes(): array [ 'open-resource' => true, // unverifiable 'non-empty-countable' => true, // bit weird, maybe a bug? + ], + [ + 'strict-array' => true, // Requires a shape + 'strict-list' => true, // Requires a shape ] ); + $basic_types['strict-array{test: 123}'] = true; + $basic_types['strict-list{123}'] = true; + return array_map( fn($type) => [$type], array_keys($basic_types) @@ -104,7 +111,7 @@ public function testTypeAcceptsType(string $parent_type_string, string $child_ty } /** - * @return array + * @return array */ public function getAllowedChildTypes(): array { @@ -122,11 +129,11 @@ public function getAllowedChildTypes(): array 'array', ], 'arrayOptionalKeyed1AcceptsEmptyArray' => [ - 'array{foo?: string}', + 'strict-array{foo?: string}', 'array', ], 'arrayOptionalKeyed2AcceptsEmptyArray' => [ - 'array{foo?: string}&array', + 'strict-array{foo?: string}&array', 'array', ], 'Lowercase-stringAndCallable-string' => [ diff --git a/tests/TypeParseTest.php b/tests/TypeParseTest.php index b6fdb3e0bc1..6865b4f9d09 100644 --- a/tests/TypeParseTest.php +++ b/tests/TypeParseTest.php @@ -190,40 +190,40 @@ public function testIntersectionOfIterables(): void public function testIntersectionOfTKeyedArray(): void { - $this->assertSame('array{a: int, b: int}', (string) Type::parseString('array{a: int}&array{b: int}')); + $this->assertSame('strict-array{a: int, b: int}', (string) Type::parseString('strict-array{a: int}&strict-array{b: int}')); } public function testIntersectionOfTwoDifferentArrays(): void { - $this->assertSame('array{a: int}', Type::parseString('array{a: int}&array')->getId()); + $this->assertSame('array{a: int}', Type::parseString('strict-array{a: int}&array')->getId()); } public function testIntersectionOfTwoDifferentArraysReversed(): void { - $this->assertSame('array{a: int}', Type::parseString('array&array{a: int}')->getId()); + $this->assertSame('array{a: int}', Type::parseString('array&strict-array{a: int}')->getId()); } public function testIntersectionOfTKeyedArrayWithMergedProperties(): void { - $this->assertSame('array{a: int}', (string) Type::parseString('array{a: int}&array{a: mixed}')); + $this->assertSame('strict-array{a: int}', (string) Type::parseString('strict-array{a: int}&strict-array{a: mixed}')); } public function testIntersectionOfTKeyedArrayWithPossiblyUndefinedMergedProperties(): void { - $this->assertSame('array{a: int}', (string) Type::parseString('array{a: int}&array{a?: int}')); + $this->assertSame('strict-array{a: int}', (string) Type::parseString('strict-array{a: int}&strict-array{a?: int}')); } public function testIntersectionOfIntranges(): void { - $this->assertSame('array{a: int<3, 4>}', (string) Type::parseString('array{a: int<2, 4>}&array{a: int<3, 6>}')); - $this->assertSame('array{a: 4}', Type::parseString('array{a: 4}&array{a: int<3, 6>}')->getId(true)); + $this->assertSame('strict-array{a: int<3, 4>}', (string) Type::parseString('strict-array{a: int<2, 4>}&strict-array{a: int<3, 6>}')); + $this->assertSame('strict-array{a: 4}', Type::parseString('strict-array{a: 4}&strict-array{a: int<3, 6>}')->getId(true)); } public function testIntersectionOfTKeyedArrayWithConflictingProperties(): void { $this->expectException(TypeParseTreeException::class); - Type::parseString('array{a: string}&array{a: int}'); + Type::parseString('strict-array{a: string}&strict-array{a: int}'); } public function testIntersectionOfTwoRegularArrays(): void @@ -234,25 +234,25 @@ public function testIntersectionOfTwoRegularArrays(): void public function testUnionOfIntersectionOfTKeyedArray(): void { - $this->assertSame('array{a: int|string, b?: int}', (string) Type::parseString('array{a: int}|array{a: string}&array{b: int}')); - $this->assertSame('array{a: int|string, b?: int}', (string) Type::parseString('array{b: int}&array{a: string}|array{a: int}')); + $this->assertSame('strict-array{a: int|string, b?: int}', (string) Type::parseString('strict-array{a: int}|strict-array{a: string}&strict-array{b: int}')); + $this->assertSame('strict-array{a: int|string, b?: int}', (string) Type::parseString('strict-array{b: int}&strict-array{a: string}|strict-array{a: int}')); } public function testIntersectionOfUnionOfTKeyedArray(): void { $this->expectException(TypeParseTreeException::class); - Type::parseString('array{a: int}&array{a: string}|array{b: int}'); + Type::parseString('strict-array{a: int}&strict-array{a: string}|strict-array{b: int}'); } public function testIntersectionOfTKeyedArrayAndObject(): void { $this->expectException(TypeParseTreeException::class); - Type::parseString('array{a: int}&T1'); + Type::parseString('strict-array{a: int}&T1'); } public function testIterableContainingTKeyedArray(): void { - $this->assertSame('iterable', Type::parseString('iterable')->getId()); + $this->assertSame('iterable', Type::parseString('iterable')->getId()); } public function testPhpDocSimpleArray(): void @@ -278,8 +278,8 @@ public function testPhpDocMultidimensionalUnionArray(): void public function testPhpDocTKeyedArray(): void { $this->assertSame( - 'array', - (string) Type::parseString('array{b: bool, d: string}[]') + 'array', + (string) Type::parseString('strict-array{b: bool, d: string}[]') ); } @@ -327,41 +327,41 @@ public function testBracketInUnion(): void public function testTKeyedArrayWithSimpleArgs(): void { - $this->assertSame('array{a: int, b: string}', (string) Type::parseString('array{a: int, b: string}')); + $this->assertSame('strict-array{a: int, b: string}', (string) Type::parseString('strict-array{a: int, b: string}')); } public function testTKeyedArrayWithSpace(): void { - $this->assertSame('array{\'a \': int, \'b \': string}', (string) Type::parseString('array{\'a \': int, \'b \': string}')); + $this->assertSame('strict-array{\'a \': int, \'b \': string}', (string) Type::parseString('strict-array{\'a \': int, \'b \': string}')); } public function testTKeyedArrayWithQuotedKeys(): void { - $this->assertSame('array{\'\\"\': int, \'\\\'\': string}', (string) Type::parseString('array{\'"\': int, \'\\\'\': string}')); - $this->assertSame('array{\'\\"\': int, \'\\\'\': string}', (string) Type::parseString('array{"\\"": int, "\\\'": string}')); + $this->assertSame('strict-array{\'\\"\': int, \'\\\'\': string}', (string) Type::parseString('strict-array{\'"\': int, \'\\\'\': string}')); + $this->assertSame('strict-array{\'\\"\': int, \'\\\'\': string}', (string) Type::parseString('strict-array{"\\"": int, "\\\'": string}')); } public function testTKeyedArrayWithClassConstantKey(): void { $this->expectException(TypeParseTreeException::class); - Type::parseString('array{self::FOO: string}'); + Type::parseString('strict-array{self::FOO: string}'); } public function testTKeyedArrayWithQuotedClassConstantKey(): void { - $this->assertSame('array{\'self::FOO\': string}', (string) Type::parseString('array{"self::FOO": string}')); + $this->assertSame('strict-array{\'self::FOO\': string}', (string) Type::parseString('strict-array{"self::FOO": string}')); } public function testTKeyedArrayWithoutClosingBracket(): void { $this->expectException(TypeParseTreeException::class); - Type::parseString('array{a: int, b: string'); + Type::parseString('strict-array{a: int, b: string'); } public function testTKeyedArrayInType(): void { $this->expectException(TypeParseTreeException::class); - Type::parseString('array{a:[]}'); + Type::parseString('strict-array{a:[]}'); } public function testObjectWithSimpleArgs(): void @@ -377,45 +377,45 @@ public function testObjectWithDollarArgs(): void public function testTKeyedArrayWithUnionArgs(): void { $this->assertSame( - 'array{a: int|string, b: string}', - (string) Type::parseString('array{a: int|string, b: string}') + 'strict-array{a: int|string, b: string}', + (string) Type::parseString('strict-array{a: int|string, b: string}') ); } public function testTKeyedArrayWithGenericArgs(): void { $this->assertSame( - 'array{a: array, b: string}', - (string) Type::parseString('array{a: array, b: string}') + 'strict-array{a: array, b: string}', + (string) Type::parseString('strict-array{a: array, b: string}') ); } public function testTKeyedArrayWithIntKeysAndUnionArgs(): void { $this->assertSame( - 'array{null|stdClass}', - (string)Type::parseString('array{stdClass|null}') + 'strict-list{null|stdClass}', + (string)Type::parseString('strict-list{stdClass|null}') ); } public function testTKeyedArrayWithIntKeysAndGenericArgs(): void { $this->assertSame( - 'array{array}', - (string)Type::parseString('array{array}') + 'strict-list{array}', + (string)Type::parseString('strict-array{array}') ); $this->assertSame( - 'array{array}', - (string)Type::parseString('array{array}') + 'strict-list{array}', + (string)Type::parseString('strict-array{array}') ); } public function testTKeyedArrayOptional(): void { $this->assertSame( - 'array{a: int, b?: int}', - (string)Type::parseString('array{a: int, b?: int}') + 'strict-array{a: int, b?: int}', + (string)Type::parseString('strict-array{a: int, b?: int}') ); } @@ -530,8 +530,8 @@ public function testConditionalTypeWithUnion(): void public function testConditionalTypeWithTKeyedArray(): void { $this->assertSame( - '(T is array{a: string} ? string : int)', - (string) Type::parseString('(T is array{a: string} ? string : int)', null, ['T' => ['' => Type::getArray()]]) + '(T is strict-array{a: string} ? string : int)', + (string) Type::parseString('(T is strict-array{a: string} ? string : int)', null, ['T' => ['' => Type::getArray()]]) ); } @@ -812,7 +812,7 @@ public function testClassStringMap(): void public function testVeryLargeType(): void { - $very_large_type = 'array{a: Closure():(array|null), b?: Closure():array, c?: Closure():array, d?: Closure():array, e?: Closure():(array{f: null|string, g: null|string, h: null|string, i: string, j: mixed, k: mixed, l: mixed, m: mixed, n: bool, o?: array{0: string}}|null), p?: Closure():(array{f: null|string, g: null|string, h: null|string, i: string, j: mixed, k: mixed, l: mixed, m: mixed, n: bool, o?: array{0: string}}|null), q: string, r?: Closure():(array|null), s: array}|null'; + $very_large_type = 'null|strict-array{a: Closure():(array|null), b?: Closure():array, c?: Closure():array, d?: Closure():array, e?: Closure():(null|strict-array{f: null|string, g: null|string, h: null|string, i: string, j: mixed, k: mixed, l: mixed, m: mixed, n: bool, o?: strict-array{0: string}}), p?: Closure():(null|strict-array{f: null|string, g: null|string, h: null|string, i: string, j: mixed, k: mixed, l: mixed, m: mixed, n: bool, o?: strict-array{0: string}}), q: string, r?: Closure():(array|null), s: array}'; $this->assertSame( $very_large_type, @@ -912,7 +912,7 @@ public function testEmptyArrayShape(): void { $this->assertSame( 'array', - (string)Type::parseString('array{}') + (string)Type::parseString('strict-array{}') ); } diff --git a/tests/TypeReconciliation/ArrayKeyExistsTest.php b/tests/TypeReconciliation/ArrayKeyExistsTest.php index 6ec62630b9a..6b4ab09c875 100644 --- a/tests/TypeReconciliation/ArrayKeyExistsTest.php +++ b/tests/TypeReconciliation/ArrayKeyExistsTest.php @@ -14,7 +14,7 @@ class ArrayKeyExistsTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -178,7 +178,7 @@ class Foo { public const F = "key"; } - /** @param array{key?: string} $a */ + /** @param strict-array{key?: string} $a */ function one(array $a): void { if (array_key_exists(Foo::F, $a)) { echo $a[Foo::F]; @@ -279,7 +279,7 @@ class pony { } /** - * @param array{0?: string, test?: string, pony?: string} $params + * @param strict-array{0?: string, test?: string, pony?: string} $params * @return string|null */ function a(array $params = []) @@ -326,7 +326,7 @@ public function getPosters($commenter, $numToGet=10) { 'arrayKeyExistsTwoVars' => [ 'code' => ' [ 'code' => ' * }>, * formatted_address: string, - * geometry: array{ - * location: array{ lat: float, lng: float }, + * geometry: strict-array{ + * location: strict-array{ lat: float, lng: float }, * location_type: string, - * viewport: array{ - * northeast: array{ lat: float, lng: float }, - * southwest: array{ lat: float, lng: float } + * viewport: strict-array{ + * northeast: strict-array{ lat: float, lng: float }, + * southwest: strict-array{ lat: float, lng: float } * } * }, * partial_match: bool, @@ -461,7 +461,7 @@ public static function getStateLabelIf(int $state): string { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -495,7 +495,7 @@ public function providerInvalidCodeParse(): iterable 'dontCreateWeirdString' => [ 'code' => ',ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -459,7 +459,7 @@ function foo(int $b): void { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/TypeReconciliation/ConditionalTest.php b/tests/TypeReconciliation/ConditionalTest.php index d5a320d9b90..50d5b319e81 100644 --- a/tests/TypeReconciliation/ConditionalTest.php +++ b/tests/TypeReconciliation/ConditionalTest.php @@ -12,7 +12,7 @@ class ConditionalTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -21,7 +21,7 @@ public function providerValidCodeParse(): iterable 'code' => ' 123]; - /** @var array{test: ?int} */ + /** @var strict-array{test: ?int} */ $a = ["test" => null]; if ($a["test"] === null) { @@ -538,7 +538,7 @@ function print_field($array) : void { } /** - * @param array{field:string,otherField:string} $array + * @param strict-array{field:string,otherField:string} $array */ function has_mix_of_fields($array) : void { print_field($array); @@ -652,7 +652,7 @@ class D extends C {} ], 'isArrayOnArrayKeyOffset' => [ 'code' => '|string>} */ + /** @var strict-array{s:array|string>} */ $doc = []; if (!is_array($doc["s"]["t"])) { @@ -1101,8 +1101,8 @@ function Foo($width, $height) : void { 'dontRewriteNullableArrayAfterEmptyCheck' => [ 'code' => ' [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -3000,7 +3000,7 @@ function foo(array $arr) : void { 'lessSpecificArrayFields' => [ 'code' => ',ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -325,7 +325,7 @@ function foo(array $a, array $b) : void { 'doubleEmptyCheckOnTKeyedArray' => [ 'code' => ' [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -486,7 +486,7 @@ function foo(bool $b) : void { ], 'preventEmptyCreatingArray' => [ 'code' => ' [ 'code' => ',ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -123,7 +123,7 @@ function assertInArray($x, $y) { return $x; }', 'assertions' => [], - 'error_level' => ['RedundantConditionGivenDocblockType', 'DocblockTypeContradiction'], + 'ignored_issues' => ['RedundantConditionGivenDocblockType', 'DocblockTypeContradiction'], ], 'assertNegatedInArrayOfNotIntersectingTypeReturnsOriginalType' => [ 'code' => ' [], - 'error_level' => ['RedundantConditionGivenDocblockType'], + 'ignored_issues' => ['RedundantConditionGivenDocblockType'], ], 'assertAgainstListOfLiteralsAndScalarUnion' => [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/TypeReconciliation/IssetTest.php b/tests/TypeReconciliation/IssetTest.php index 95bc2cceeba..814c1b10d07 100644 --- a/tests/TypeReconciliation/IssetTest.php +++ b/tests/TypeReconciliation/IssetTest.php @@ -12,7 +12,7 @@ class IssetTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -289,8 +289,8 @@ function takesInt(int $i) : void {}', 'returnArrayWithDefinedKeys' => [ 'code' => ' [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -1125,7 +1125,7 @@ function foo(int $i) : ?string { 'accessAfterIssetCheckOnFalsableArray' => [ 'code' => ' "hello"] : []); diff --git a/tests/TypeReconciliation/ReconcilerTest.php b/tests/TypeReconciliation/ReconcilerTest.php index 4f86dbbc9c0..0f910f8d680 100644 --- a/tests/TypeReconciliation/ReconcilerTest.php +++ b/tests/TypeReconciliation/ReconcilerTest.php @@ -106,7 +106,7 @@ public function testTypeIsContainedBy($input, $container): void } /** - * @return array + * @return array */ public function providerTestReconcilation(): array { @@ -175,7 +175,7 @@ public function providerTestReconcilation(): array 'classAssertionOnClassInterfaceUnion' => ['SomeClass|SomeClass&SomeInterface', new IsType(new TNamedObject('SomeClass')), 'SomeClass|SomeInterface'], 'stringToNumericStringWithInt' => ['numeric-string', new IsLooselyEqual(new TInt()), 'string'], 'stringToNumericStringWithFloat' => ['numeric-string', new IsLooselyEqual(new TFloat()), 'string'], - 'filterKeyedArrayWithIterable' => ['array{some: string}',new IsType(new TIterable([Type::getMixed(), Type::getString()])), 'array{some: mixed}'], + 'filterKeyedArrayWithIterable' => ['strict-array{some: string}',new IsType(new TIterable([Type::getMixed(), Type::getString()])), 'strict-array{some: mixed}'], 'SimpleXMLElementNotAlwaysTruthy' => ['SimpleXMLElement', new Truthy(), 'SimpleXMLElement'], 'SimpleXMLElementNotAlwaysTruthy2' => ['SimpleXMLElement', new Falsy(), 'SimpleXMLElement'], 'SimpleXMLIteratorNotAlwaysTruthy' => ['SimpleXMLIterator', new Truthy(), 'SimpleXMLIterator'], @@ -185,7 +185,7 @@ public function providerTestReconcilation(): array } /** - * @return array + * @return array */ public function providerTestTypeIsContainedBy(): array { @@ -200,12 +200,12 @@ public function providerTestTypeIsContainedBy(): array 'unionContainsWithstring' => ['string', 'string|false'], 'unionContainsWithFalse' => ['false', 'string|false'], 'objectLikeTypeWithPossiblyUndefinedToGeneric' => [ - 'array{0: array{a: string}, 1: array{c: string, e: string}}', + 'strict-array{0: strict-array{a: string}, 1: strict-array{c: string, e: string}}', 'array>', ], 'objectLikeTypeWithPossiblyUndefinedToEmpty' => [ 'array', - 'array{a?: string, b?: string}', + 'strict-array{a?: string, b?: string}', ], 'literalNumericStringInt' => [ '"0"', @@ -256,7 +256,7 @@ class Foo } /** - * @return array + * @return array */ public function constantAssertions(): array { diff --git a/tests/TypeReconciliation/RedundantConditionTest.php b/tests/TypeReconciliation/RedundantConditionTest.php index 1038e354c87..72e43f547d3 100644 --- a/tests/TypeReconciliation/RedundantConditionTest.php +++ b/tests/TypeReconciliation/RedundantConditionTest.php @@ -14,7 +14,7 @@ class RedundantConditionTest extends TestCase use InvalidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -337,7 +337,7 @@ function getStrings(): array { $a = getStrings(); if (is_string($a[0]) && strlen($a[0]) > 3) {}', - 'assignments' => [], + 'assertions' => [], 'ignored_issues' => [], ], 'nullToMixedWithNullCheckWithIntKey' => [ @@ -350,7 +350,7 @@ function getStrings(): array { $a = getStrings(); if (is_string($a[0]) && strlen($a[0]) > 3) {}', - 'assignments' => [], + 'assertions' => [], 'ignored_issues' => [], ], 'replaceFalseTypeWithTrueConditionalOnMixedEquality' => [ @@ -368,7 +368,7 @@ function getData() { if ($a === false) {} }', - 'assignments' => [], + 'assertions' => [], 'ignored_issues' => ['MixedAssignment', 'MissingReturnType', 'MixedArrayAccess'], ], 'nullCoalescePossiblyUndefined' => [ @@ -381,7 +381,7 @@ function getData() { $option = $options["option"] ?? false; if ($option) {}', - 'assignments' => [], + 'assertions' => [], 'ignored_issues' => ['MixedAssignment', 'MixedArrayAccess'], ], 'allowIntValueCheckAfterComparisonDueToOverflow' => [ @@ -894,7 +894,7 @@ public function get(): ?stdClass{ return new stdClass;} 'secondFalsyTwiceWithChange' => [ 'code' => ',php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -1517,7 +1517,7 @@ public function get(): stdClass{ return new stdClass;} 'secondFalsyTwiceWithoutChange' => [ 'code' => ' [ 'code' => ',ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -215,7 +215,7 @@ function a(): ?int { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/TypeReconciliation/TypeAlgebraTest.php b/tests/TypeReconciliation/TypeAlgebraTest.php index c3cae983726..b5e0a31e137 100644 --- a/tests/TypeReconciliation/TypeAlgebraTest.php +++ b/tests/TypeReconciliation/TypeAlgebraTest.php @@ -12,7 +12,7 @@ class TypeAlgebraTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -1218,7 +1218,7 @@ function test(string|object $s, bool $b) : string { } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/TypeReconciliation/TypeTest.php b/tests/TypeReconciliation/TypeTest.php index e200dc8fe0b..6f3cbe565c0 100644 --- a/tests/TypeReconciliation/TypeTest.php +++ b/tests/TypeReconciliation/TypeTest.php @@ -14,11 +14,117 @@ class TypeTest extends TestCase use ValidCodeAnalysisTestTrait; /** - * @return iterable,ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { return [ + 'sealArray' => [ + 'code' => ' [ + '$a===' => 'strict-array{a: mixed}' + ] + ], + 'sealedArrayCount' => [ + 'code' => ' [ + '$a===' => 'strict-list{0?: 0, 1?: 1}', + '$b===' => 'null|strict-list{0, 1}' + ] + ], + 'sealedArrayMagic' => [ + 'code' => ' $time + */ + function mapTime($time): void + { + $atime = is_array($time) ? $time : []; + if ($time === "24h") { + return; + } + + for ($day = 0; $day < 7; ++$day) { + if (!array_key_exists($day, $atime) || !is_array($atime[$day])) { + continue; + } + + $dayWh = $atime[$day]; + array_pop($dayWh); + } + }', + 'assertions' => [ + '$buttons===' => 'list', + '$urls===' => 'strict-list{0?: non-falsy-string}', + '$mainUrlSet===' => 'bool', + ] + ], + 'validSealedArrayAssertions' => [ + 'code' => ' 2) { + echo "Have C!"; + } + + if (count($a) < 3) { + echo "Do not have C!"; + } + ', + ], + 'validSealedArrayAssertions2' => [ + 'code' => ' 2); + ', + 'assertions' => [ + '$a===' => 'strict-array{a: string, b: string, c: string}', + ] + ], 'instanceOfInterface' => [ 'code' => ',php_version?:string}> + * @return iterable,php_version?:string}> */ public function providerInvalidCodeParse(): iterable { @@ -1570,6 +1676,60 @@ function returnsFalse() { return rand() % 2 > 0; } ', 'error_message' => 'InvalidReturnStatement', ], + 'invalidSealedArrayAssertion1' => [ + 'code' => ' 1) { + }', + 'error_message' => 'RedundantConditionGivenDocblockType' + ], + 'invalidSealedArrayAssertion2' => [ + 'code' => ' 3) { + }', + 'error_message' => 'DocblockTypeContradiction' + ], + 'invalidSealedArrayAssertion3' => [ + 'code' => ' 4) { + }', + 'error_message' => 'DocblockTypeContradiction' + ], + 'invalidSealedArrayAssertion4' => [ + 'code' => ' 'RedundantConditionGivenDocblockType' + ], + 'invalidSealedArrayAssertion5' => [ + 'code' => ' 'DocblockTypeContradiction' + ], + 'invalidSealedArrayAssertion6' => [ + 'code' => ' 'RedundantConditionGivenDocblockType' + ], 'intersectionTypeClassCheckAfterInstanceof' => [ 'code' => ',ignored_issues?:list}> + * */ public function providerValidCodeParse(): iterable { @@ -569,7 +569,7 @@ function foo($s) : void { 'code' => ' false, "to" => false]; @@ -589,7 +589,7 @@ public function foo(string ...$things) : void { 'code' => ' false, "to" => false]; @@ -802,7 +802,7 @@ function foo(int $a): void { 'returnFromUnionLiteral' => [ 'code' => ' [ 'code' => ' + * */ public function providerInvalidCodeParse(): iterable { diff --git a/tests/UnresolvableIncludeTest.php b/tests/UnresolvableIncludeTest.php index 1f1ec2da166..3eb2cf0ef48 100644 --- a/tests/UnresolvableIncludeTest.php +++ b/tests/UnresolvableIncludeTest.php @@ -22,7 +22,7 @@ public function testShouldThrowUnresolvableInclude(string $phpCode, int $expecte } /** - * @return array + * @return array */ public function providerUnresolvableInclude(): array { @@ -51,7 +51,7 @@ public function testShouldNotThrowUnresolvableInclude(string $phpCode): void } /** - * @return array + * @return array */ public function providerNoUnresolvableInclude(): array { diff --git a/tests/UnusedCodeTest.php b/tests/UnusedCodeTest.php index 9484452be63..8e5e2892e60 100644 --- a/tests/UnusedCodeTest.php +++ b/tests/UnusedCodeTest.php @@ -45,10 +45,10 @@ public function setUp(): void * @dataProvider providerValidCodeParse * * @param string $code - * @param array $error_levels + * @param array $ignored_issues * */ - public function testValidCode($code, array $error_levels = []): void + public function testValidCode($code, array $ignored_issues = []): void { $test_name = $this->getTestName(); if (strpos($test_name, 'SKIPPED-') !== false) { @@ -64,7 +64,7 @@ public function testValidCode($code, array $error_levels = []): void $this->project_analyzer->setPhpVersion('8.0', 'tests'); - foreach ($error_levels as $error_level) { + foreach ($ignored_issues as $error_level) { $this->project_analyzer->getCodebase()->config->setCustomErrorLevel($error_level, Config::REPORT_SUPPRESS); } @@ -80,10 +80,10 @@ public function testValidCode($code, array $error_levels = []): void * * @param string $code * @param string $error_message - * @param array $error_levels + * @param array $ignored_issues * */ - public function testInvalidCode($code, $error_message, $error_levels = []): void + public function testInvalidCode($code, $error_message, $ignored_issues = []): void { if (strpos($this->getTestName(), 'SKIPPED-') !== false) { $this->markTestSkipped(); @@ -94,7 +94,7 @@ public function testInvalidCode($code, $error_message, $error_levels = []): void $file_path = self::$src_dir_path . 'somefile.php'; - foreach ($error_levels as $error_level) { + foreach ($ignored_issues as $error_level) { $this->project_analyzer->getCodebase()->config->setCustomErrorLevel($error_level, Config::REPORT_SUPPRESS); } @@ -175,7 +175,7 @@ function bar(): void{ } /** - * @return array + * @return array */ public function providerValidCodeParse(): array { @@ -1211,7 +1211,7 @@ class A { } /** - * @return array}> + * @return array}> */ public function providerInvalidCodeParse(): array { diff --git a/tests/UnusedVariableTest.php b/tests/UnusedVariableTest.php index 6bc30bfd38d..f6b2c5276f9 100644 --- a/tests/UnusedVariableTest.php +++ b/tests/UnusedVariableTest.php @@ -42,10 +42,10 @@ public function setUp(): void * @dataProvider providerValidCodeParse * * @param string $code - * @param array $error_levels + * @param array $ignored_issues * */ - public function testValidCode($code, array $error_levels = [], string $php_version = '7.4'): void + public function testValidCode($code, array $ignored_issues = [], string $php_version = '7.4'): void { $test_name = $this->getTestName(); if (strpos($test_name, 'SKIPPED-') !== false) { @@ -61,7 +61,7 @@ public function testValidCode($code, array $error_levels = [], string $php_versi $code ); - foreach ($error_levels as $error_level) { + foreach ($ignored_issues as $error_level) { $this->project_analyzer->getCodebase()->config->setCustomErrorLevel($error_level, Config::REPORT_SUPPRESS); } @@ -73,10 +73,10 @@ public function testValidCode($code, array $error_levels = [], string $php_versi * * @param string $code * @param string $error_message - * @param array $error_levels + * @param array $ignored_issues * */ - public function testInvalidCode($code, $error_message, $error_levels = []): void + public function testInvalidCode($code, $error_message, $ignored_issues = []): void { if (strpos($this->getTestName(), 'SKIPPED-') !== false) { $this->markTestSkipped(); @@ -89,7 +89,7 @@ public function testInvalidCode($code, $error_message, $error_levels = []): void $file_path = self::$src_dir_path . 'somefile.php'; - foreach ($error_levels as $error_level) { + foreach ($ignored_issues as $error_level) { $this->project_analyzer->getCodebase()->config->setCustomErrorLevel($error_level, Config::REPORT_SUPPRESS); } @@ -102,7 +102,7 @@ public function testInvalidCode($code, $error_message, $error_levels = []): void } /** - * @return array,php_version?:string}> + * @return array,php_version?:string}> */ public function providerValidCodeParse(): array { @@ -744,7 +744,7 @@ function main() : void { 'arrayVarAssignmentInFunctionAndReturned' => [ 'code' => ' [ 'code' => ' $arr + * @param list $arr */ function far(array $arr): void { foreach ($arr as [$a, $b]) { @@ -767,7 +767,6 @@ function far(array $arr): void { 'arraySubAppend' => [ 'code' => ' []]; foreach ($rules as $rule) { $report["runs"][] = $rule; @@ -2412,7 +2411,7 @@ function bar(): int { } } }', - 'error_levels' => [], + 'ignored_issues' => [], 'php_version' => '8.0', ], 'concatWithUnknownProperty' => [ @@ -2528,10 +2527,6 @@ function setProxySettingsFromEnv(): void { $b = 2; echo $a; ', - 'assertions' => [ - '$b===' => '2', - '$a===' => '2', - ], ], 'referenceUsedAfterVariableReassignment' => [ 'code' => ' + * @return array */ public function providerInvalidCodeParse(): array { @@ -3595,7 +3590,7 @@ function takesArray($a) : void { $arr = [$a]; takesArrayOfString($arr); }', - 'error_message' => 'MixedArgumentTypeCoercion - src' . DIRECTORY_SEPARATOR . 'somefile.php:12:44 - Argument 1 of takesArrayOfString expects array, but parent type array{mixed} provided. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:10:41' + 'error_message' => 'MixedArgumentTypeCoercion - src' . DIRECTORY_SEPARATOR . 'somefile.php:12:44 - Argument 1 of takesArrayOfString expects array, but parent type strict-list{mixed} provided. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:10:41' ], 'warnAboutUnusedVariableInTryReassignedInCatch' => [ 'code' => ',ignored_issues?:list,php_version?:string}> + * */ public function providerValidCodeParse(): iterable { @@ -114,7 +114,7 @@ function getKey2() { 'acceptLiteralIntInValueOfUnionLiteralInts' => [ 'code' => '|array{0: 3, 1: 4}> + * @return value-of|strict-array{0: 3, 1: 4}> */ function getValue(int $i) { if ($i >= 0 && $i <= 4) { @@ -213,7 +213,7 @@ function foobar(int|string $arg): void } /** - * @return iterable,php_version?:string}> + * */ public function providerInvalidCodeParse(): iterable { @@ -261,7 +261,7 @@ public function getValue() { 'noOtherStringAllowedForValueOfKeyedArray' => [ 'code' => ' + * @return value-of */ function getValue() { return "adams"; @@ -272,7 +272,7 @@ function getValue() { 'noOtherIntAllowedInValueOfUnionLiteralInts' => [ 'code' => '|array{0: 3, 1: 4}> + * @return value-of|strict-array{0: 3, 1: 4}> */ function getValue() { return 5; diff --git a/tests/VariadicTest.php b/tests/VariadicTest.php index ab3b38328b4..2b1b6c087a5 100644 --- a/tests/VariadicTest.php +++ b/tests/VariadicTest.php @@ -71,7 +71,7 @@ public function testVariadicFunctionFromAutoloadFile(): void } /** - * @return iterable + * @return iterable */ public function providerValidCodeParse(): iterable {