diff --git a/composer.json b/composer.json index fd33b774344..e178df0697a 100644 --- a/composer.json +++ b/composer.json @@ -60,6 +60,10 @@ "ext-curl": "In order to send data to shepherd" }, "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true, + "composer/package-versions-deprecated": true + }, "optimize-autoloader": true, "sort-packages": true, "platform-check": false diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index e4695f2c742..5fe1ec4217d 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -6824,7 +6824,7 @@ 'ldap_sasl_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'string', 'password='=>'string', 'mech='=>'string', 'realm='=>'string', 'authc_id='=>'string', 'authz_id='=>'string', 'props='=>'string'], 'ldap_search' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_set_option' => ['bool', 'ldap'=>'LDAP\Connection|null', 'option'=>'int', 'value'=>'mixed'], -'ldap_set_rebind_proc' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'string'], +'ldap_set_rebind_proc' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], 'ldap_start_tls' => ['bool', 'ldap'=>'resource'], 'ldap_t61_to_8859' => ['string', 'value'=>'string'], 'ldap_unbind' => ['bool', 'ldap'=>'resource'], @@ -8393,7 +8393,7 @@ 'mysqli::__construct' => ['void', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null'], 'mysqli::autocommit' => ['bool', 'enable'=>'bool'], 'mysqli::begin_transaction' => ['bool', 'flags='=>'int', 'name='=>'?string'], -'mysqli::change_user' => ['bool', 'username'=>'string', 'password'=>'string', 'database'=>'string'], +'mysqli::change_user' => ['bool', 'username'=>'string', 'password'=>'string', 'database'=>'?string'], 'mysqli::character_set_name' => ['string'], 'mysqli::close' => ['bool'], 'mysqli::commit' => ['bool', 'flags='=>'int', 'name='=>'?string'], @@ -8431,7 +8431,7 @@ 'mysqli::set_local_infile_default' => ['void'], 'mysqli::set_local_infile_handler' => ['bool', 'read_func='=>'callable'], 'mysqli::set_opt' => ['bool', 'option'=>'int', 'value'=>'string|int'], -'mysqli::ssl_set' => ['bool', 'key'=>'string', 'certificate'=>'string', 'ca_certificate'=>'string', 'ca_path'=>'string', 'cipher_algos'=>'string'], +'mysqli::ssl_set' => ['bool', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'], 'mysqli::stat' => ['string|false'], 'mysqli::stmt_init' => ['mysqli_stmt'], 'mysqli::store_result' => ['mysqli_result|false', 'mode='=>'int'], @@ -8483,7 +8483,7 @@ 'mysqli_free_result' => ['void', 'result'=>'mysqli_result'], 'mysqli_get_cache_stats' => ['array|false'], 'mysqli_get_charset' => ['object', 'mysql'=>'mysqli'], -'mysqli_get_client_info' => ['string', 'mysql'=>'mysqli'], +'mysqli_get_client_info' => ['string', 'mysql='=>'?mysqli'], 'mysqli_get_client_stats' => ['array'], 'mysqli_get_client_version' => ['int', 'link'=>'mysqli'], 'mysqli_get_connection_stats' => ['array', 'mysql'=>'mysqli'], @@ -8549,7 +8549,7 @@ 'mysqli_set_opt' => ['bool', 'mysql'=>'mysqli', 'option'=>'int', 'value'=>'string|int'], 'mysqli_slave_query' => ['bool', 'link'=>'mysqli', 'query'=>'string'], 'mysqli_sqlstate' => ['string', 'mysql'=>'mysqli'], -'mysqli_ssl_set' => ['bool', 'mysql'=>'mysqli', 'key'=>'string', 'certificate'=>'string', 'ca_certificate'=>'string', 'ca_path'=>'string', 'cipher_algos'=>'string'], +'mysqli_ssl_set' => ['bool', 'mysql'=>'mysqli', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'], 'mysqli_stat' => ['string|false', 'mysql'=>'mysqli'], 'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query'=>'string'], 'mysqli_stmt::attr_get' => ['int', 'attribute'=>'int'], @@ -9025,8 +9025,7 @@ 'nsapi_response_headers' => ['array'], 'nsapi_virtual' => ['bool', 'uri'=>'string'], 'nthmac' => ['string', 'clent'=>'string', 'data'=>'string'], -'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int'], -'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'string', 'thousands_separator'=>'string'], +'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string', 'thousands_separator='=>'string'], 'NumberFormatter::__construct' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::create' => ['NumberFormatter|false', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::format' => ['string|false', 'num'=>'', 'type='=>'int'], @@ -11460,6 +11459,7 @@ 'ReflectionFunctionAbstract::isDeprecated' => ['bool'], 'ReflectionFunctionAbstract::isGenerator' => ['bool'], 'ReflectionFunctionAbstract::isInternal' => ['bool'], +'ReflectionFunctionAbstract::isStatic' => ['bool'], 'ReflectionFunctionAbstract::isUserDefined' => ['bool'], 'ReflectionFunctionAbstract::isVariadic' => ['bool'], 'ReflectionFunctionAbstract::returnsReference' => ['bool'], @@ -11509,7 +11509,6 @@ 'ReflectionMethod::isPrivate' => ['bool'], 'ReflectionMethod::isProtected' => ['bool'], 'ReflectionMethod::isPublic' => ['bool'], -'ReflectionMethod::isStatic' => ['bool'], 'ReflectionMethod::isUserDefined' => ['bool'], 'ReflectionMethod::isVariadic' => ['bool'], 'ReflectionMethod::returnsReference' => ['bool'], @@ -12244,19 +12243,19 @@ 'SoapClient::__construct' => ['void', 'wsdl'=>'mixed', 'options='=>'array|null'], 'SoapClient::__doRequest' => ['?string', 'request'=>'string', 'location'=>'string', 'action'=>'string', 'version'=>'int', 'one_way='=>'bool'], 'SoapClient::__getCookies' => ['array'], -'SoapClient::__getFunctions' => ['array'], -'SoapClient::__getLastRequest' => ['string'], -'SoapClient::__getLastRequestHeaders' => ['string'], -'SoapClient::__getLastResponse' => ['string'], -'SoapClient::__getLastResponseHeaders' => ['string'], -'SoapClient::__getTypes' => ['array'], +'SoapClient::__getFunctions' => ['?array'], +'SoapClient::__getLastRequest' => ['?string'], +'SoapClient::__getLastRequestHeaders' => ['?string'], +'SoapClient::__getLastResponse' => ['?string'], +'SoapClient::__getLastResponseHeaders' => ['?string'], +'SoapClient::__getTypes' => ['?array'], 'SoapClient::__setCookie' => ['', 'name'=>'string', 'value='=>'string'], 'SoapClient::__setLocation' => ['string', 'new_location='=>'string'], 'SoapClient::__setSoapHeaders' => ['bool', 'soapheaders='=>''], 'SoapClient::__soapCall' => ['', 'function_name'=>'string', 'arguments'=>'array', 'options='=>'array', 'input_headers='=>'SoapHeader|array', '&w_output_headers='=>'array'], 'SoapClient::SoapClient' => ['object', 'wsdl'=>'mixed', 'options='=>'array|null'], 'SoapFault::__clone' => ['void'], -'SoapFault::__construct' => ['void', 'faultcode'=>'string', 'faultstring'=>'string', 'faultactor='=>'string', 'detail='=>'string', 'faultname='=>'string', 'headerfault='=>'string'], +'SoapFault::__construct' => ['void', 'code'=>'array|string|null', 'string'=>'string', 'actor='=>'?string', 'details='=>'?mixed', 'name='=>'?string', 'headerFault='=>'?mixed'], 'SoapFault::__toString' => ['string'], 'SoapFault::__wakeup' => ['void'], 'SoapFault::getCode' => ['int'], @@ -12266,7 +12265,7 @@ 'SoapFault::getPrevious' => ['?Exception|?Throwable'], 'SoapFault::getTrace' => ['list>'], 'SoapFault::getTraceAsString' => ['string'], -'SoapFault::SoapFault' => ['object', 'faultcode'=>'string', 'faultstring'=>'string', 'faultactor='=>'string', 'detail='=>'string', 'faultname='=>'string', 'headerfault='=>'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'], 'SoapHeader::SoapHeader' => ['object', 'namespace'=>'string', 'name'=>'string', 'data='=>'mixed', 'mustunderstand='=>'bool', 'actor='=>'string'], 'SoapParam::__construct' => ['void', 'data'=>'mixed', 'name'=>'string'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index 9402f8b84a2..f561a75ea15 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -713,6 +713,10 @@ 'old' => ['bool|string', 'ldap'=>'resource', 'user='=>'string', 'old_password='=>'string', 'new_password='=>'string', '&w_controls='=>'array'], 'new' => ['bool|string', 'ldap'=>'resource', 'user='=>'string', 'old_password='=>'string', 'new_password='=>'string', '&w_controls='=>'array|null'], ], + 'ldap_set_rebind_proc' => [ + 'old' => ['bool', 'ldap'=>'resource', 'callback'=>'callable'], + 'new' => ['bool', 'ldap'=>'resource', 'callback'=>'?callable'], + ], 'mb_check_encoding' => [ 'old' => ['bool', 'value='=>'array|string', 'encoding='=>'string'], 'new' => ['bool', 'value='=>'array|string|null', 'encoding='=>'string|null'], @@ -949,6 +953,10 @@ 'old' => ['bool', 'mysql'=>'mysqli', 'flags='=>'int', 'name='=>'string'], 'new' => ['bool', 'mysql'=>'mysqli', 'flags='=>'int', 'name='=>'?string'], ], + 'number_format' => [ + 'old' => ['string', 'num'=>'float|int', 'decimals='=>'int'], + 'new' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'string', 'thousands_separator='=>'string'], + ], 'openssl_csr_export' => [ 'old' => ['bool', 'csr'=>'string|resource', '&w_output'=>'string', 'no_text='=>'bool'], 'new' => ['bool', 'csr'=>'OpenSSLCertificateSigningRequest|string', '&w_output'=>'OpenSSLAsymmetricKey', 'no_text='=>'bool'], @@ -1591,6 +1599,7 @@ 'image2wbmp' => ['bool', 'im'=>'resource', 'filename='=>'?string', 'threshold='=>'int'], 'jpeg2wbmp' => ['bool', 'jpegname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'], 'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'], + 'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'string', 'thousands_separator'=>'string'], 'png2wbmp' => ['bool', 'pngname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'], 'read_exif_data' => ['array', 'filename'=>'string', 'sections_needed='=>'string', 'sub_arrays='=>'bool', 'read_thumbnail='=>'bool'], 'SimpleXMLIterator::rewind' => ['void'], diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index efa975bd3fc..81bc19a890f 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -45,6 +45,7 @@ 'ReflectionEnumUnitCase::getEnum' => ['ReflectionEnum'], 'ReflectionEnumUnitCase::getValue' => ['UnitEnum'], 'ReflectionEnumBackedCase::getBackingValue' => ['string|int'], + 'ReflectionFunctionAbstract::isStatic' => ['bool'], ], 'changed' => [ @@ -633,8 +634,8 @@ 'new' => ['bool', 'ldap'=>'LDAP\Connection|null', 'option'=>'int', 'value'=>'mixed'], ], 'ldap_set_rebind_proc' => [ - 'old' => ['bool', 'ldap'=>'resource', 'callback'=>'string'], - 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'string'], + 'old' => ['bool', 'ldap'=>'resource', 'callback'=>'?callable'], + 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], ], 'mysqli::connect' => [ 'old' => ['null|false', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null'], @@ -1106,5 +1107,7 @@ ], ], - 'removed' => [], + 'removed' => [ + 'ReflectionMethod::isStatic' => ['bool'], + ], ]; diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index c69ed589017..42e4bb5fcb8 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -6823,19 +6823,19 @@ 'SoapClient::__construct' => ['void', 'wsdl'=>'mixed', 'options='=>'array|null'], 'SoapClient::__doRequest' => ['?string', 'request'=>'string', 'location'=>'string', 'action'=>'string', 'version'=>'int', 'one_way='=>'int'], 'SoapClient::__getCookies' => ['array'], - 'SoapClient::__getFunctions' => ['array'], - 'SoapClient::__getLastRequest' => ['string'], - 'SoapClient::__getLastRequestHeaders' => ['string'], - 'SoapClient::__getLastResponse' => ['string'], - 'SoapClient::__getLastResponseHeaders' => ['string'], - 'SoapClient::__getTypes' => ['array'], + 'SoapClient::__getFunctions' => ['?array'], + 'SoapClient::__getLastRequest' => ['?string'], + 'SoapClient::__getLastRequestHeaders' => ['?string'], + 'SoapClient::__getLastResponse' => ['?string'], + 'SoapClient::__getLastResponseHeaders' => ['?string'], + 'SoapClient::__getTypes' => ['?array'], 'SoapClient::__setCookie' => ['', 'name'=>'string', 'value='=>'string'], 'SoapClient::__setLocation' => ['string', 'new_location='=>'string'], 'SoapClient::__setSoapHeaders' => ['bool', 'soapheaders='=>''], 'SoapClient::__soapCall' => ['', 'function_name'=>'string', 'arguments'=>'array', 'options='=>'array', 'input_headers='=>'SoapHeader|array', '&w_output_headers='=>'array'], - 'SoapFault::SoapFault' => ['object', 'faultcode'=>'string', 'faultstring'=>'string', 'faultactor='=>'string', 'detail='=>'string', 'faultname='=>'string', 'headerfault='=>'string'], + 'SoapFault::SoapFault' => ['object', 'faultcode'=>'string', 'faultstring'=>'string', 'faultactor='=>'?string', 'detail='=>'?mixed', 'faultname='=>'?string', 'headerfault='=>'?mixed'], 'SoapFault::__clone' => ['void'], - 'SoapFault::__construct' => ['void', 'faultcode'=>'string', 'faultstring'=>'string', 'faultactor='=>'string', 'detail='=>'string', 'faultname='=>'string', 'headerfault='=>'string'], + 'SoapFault::__construct' => ['void', 'code'=>'array|string|null', 'string'=>'string', 'actor='=>'?string', 'details='=>'?mixed', 'name='=>'?string', 'headerFault='=>'?mixed'], 'SoapFault::__toString' => ['string'], 'SoapFault::__wakeup' => ['void'], 'SoapFault::getCode' => ['int'], @@ -12525,7 +12525,7 @@ 'ldap_sasl_bind' => ['bool', 'ldap'=>'resource', 'dn='=>'string', 'password='=>'string', 'mech='=>'string', 'realm='=>'string', 'authc_id='=>'string', 'authz_id='=>'string', 'props='=>'string'], 'ldap_search' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_set_option' => ['bool', 'ldap'=>'resource|null', 'option'=>'int', 'value'=>'mixed'], - 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'resource', 'callback'=>'string'], + 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'resource', 'callback'=>'callable'], 'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'], 'ldap_start_tls' => ['bool', 'ldap'=>'resource'], 'ldap_t61_to_8859' => ['string', 'value'=>'string'], @@ -13329,7 +13329,7 @@ 'mysqli::__construct' => ['void', 'hostname='=>'string', 'username='=>'string', 'password='=>'string', 'database='=>'string', 'port='=>'int', 'socket='=>'string'], 'mysqli::autocommit' => ['bool', 'enable'=>'bool'], 'mysqli::begin_transaction' => ['bool', 'flags='=>'int', 'name='=>'string'], - 'mysqli::change_user' => ['bool', 'username'=>'string', 'password'=>'string', 'database'=>'string'], + 'mysqli::change_user' => ['bool', 'username'=>'string', 'password'=>'string', 'database'=>'?string'], 'mysqli::character_set_name' => ['string'], 'mysqli::close' => ['bool'], 'mysqli::commit' => ['bool', 'flags='=>'int', 'name='=>'string'], @@ -13367,7 +13367,7 @@ 'mysqli::set_local_infile_default' => ['void'], 'mysqli::set_local_infile_handler' => ['bool', 'read_func='=>'callable'], 'mysqli::set_opt' => ['bool', 'option'=>'int', 'value'=>'string|int'], - 'mysqli::ssl_set' => ['bool', 'key'=>'string', 'certificate'=>'string', 'ca_certificate'=>'string', 'ca_path'=>'string', 'cipher_algos'=>'string'], + 'mysqli::ssl_set' => ['bool', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'], 'mysqli::stat' => ['string|false'], 'mysqli::stmt_init' => ['mysqli_stmt'], 'mysqli::store_result' => ['mysqli_result|false', 'mode='=>'int'], @@ -13418,7 +13418,7 @@ 'mysqli_free_result' => ['void', 'result'=>'mysqli_result'], 'mysqli_get_cache_stats' => ['array|false'], 'mysqli_get_charset' => ['object', 'mysql'=>'mysqli'], - 'mysqli_get_client_info' => ['string', 'mysql'=>'mysqli'], + 'mysqli_get_client_info' => ['string', 'mysql='=>'?mysqli'], 'mysqli_get_client_stats' => ['array'], 'mysqli_get_client_version' => ['int', 'link'=>'mysqli'], 'mysqli_get_connection_stats' => ['array', 'mysql'=>'mysqli'], @@ -13483,7 +13483,7 @@ 'mysqli_set_opt' => ['bool', 'mysql'=>'mysqli', 'option'=>'int', 'value'=>'string|int'], 'mysqli_slave_query' => ['bool', 'link'=>'mysqli', 'query'=>'string'], 'mysqli_sqlstate' => ['string', 'mysql'=>'mysqli'], - 'mysqli_ssl_set' => ['bool', 'mysql'=>'mysqli', 'key'=>'string', 'certificate'=>'string', 'ca_certificate'=>'string', 'ca_path'=>'string', 'cipher_algos'=>'string'], + 'mysqli_ssl_set' => ['bool', 'mysql'=>'mysqli', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'], 'mysqli_stat' => ['string|false', 'mysql'=>'mysqli'], 'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query'=>'string'], 'mysqli_stmt::attr_get' => ['int', 'attribute'=>'int'], diff --git a/dictionaries/InternalTaintSinkMap.php b/dictionaries/InternalTaintSinkMap.php index f161e9de6e1..3838720a2fb 100644 --- a/dictionaries/InternalTaintSinkMap.php +++ b/dictionaries/InternalTaintSinkMap.php @@ -1,9 +1,11 @@ >> + * @var array>> */ return [ 'exec' => [['shell']], diff --git a/dictionaries/PropertyMap.php b/dictionaries/PropertyMap.php index 15244e1162f..b97344960de 100644 --- a/dictionaries/PropertyMap.php +++ b/dictionaries/PropertyMap.php @@ -527,11 +527,12 @@ 'headers' => 'array|null', ], 'soapfault' => [ - 'faultcode' => 'string', + 'faultcode' => 'string|null', + 'faultcodens' => 'string|null', 'faultstring' => 'string', - 'faultactor' => 'string', - 'detail' => 'string', + 'faultactor' => 'string|null', + 'detail' => 'mixed|null', '_name' => 'string', - 'headerfault' => 'string', + 'headerfault' => 'mixed|null', ], ]; diff --git a/docs/running_psalm/issues/DeprecatedConstant.md b/docs/running_psalm/issues/DeprecatedConstant.md index e8965dbb3bb..676f524e23e 100644 --- a/docs/running_psalm/issues/DeprecatedConstant.md +++ b/docs/running_psalm/issues/DeprecatedConstant.md @@ -1,6 +1,6 @@ # DeprecatedConstant -Emitted when referring to a deprecated constant: +Emitted when referring to a deprecated constant or enum case: ```php checkMethod($method_id, $first_stmt, $codebase); @@ -57,8 +60,8 @@ public function analyze(?Context $file_context = null, ?Context $global_context return; } - $this_params->vars_in_scope['$this'] = new Type\Union([ - new Type\Atomic\TNamedObject(self::VIEW_CLASS), + $this_params->vars_in_scope['$this'] = new Union([ + new TNamedObject(self::VIEW_CLASS), ]); } } @@ -67,8 +70,8 @@ public function analyze(?Context $file_context = null, ?Context $global_context $this_params = new Context(); $this_params->check_variables = false; $this_params->self = self::VIEW_CLASS; - $this_params->vars_in_scope['$this'] = new Type\Union([ - new Type\Atomic\TNamedObject(self::VIEW_CLASS), + $this_params->vars_in_scope['$this'] = new Union([ + new TNamedObject(self::VIEW_CLASS), ]); } @@ -78,7 +81,7 @@ public function analyze(?Context $file_context = null, ?Context $global_context /** * @return Context|false */ - private function checkMethod(\Psalm\Internal\MethodIdentifier $method_id, PhpParser\Node $stmt, Codebase $codebase) + private function checkMethod(MethodIdentifier $method_id, PhpParser\Node $stmt, Codebase $codebase) { if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName( $this, @@ -98,16 +101,16 @@ private function checkMethod(\Psalm\Internal\MethodIdentifier $method_id, PhpPar $class_storage = $codebase->classlike_storage_provider->get($method_id->fq_class_name); - $this_context->vars_in_scope['$this'] = new Type\Union([new Type\Atomic\TNamedObject($class_storage->name)]); + $this_context->vars_in_scope['$this'] = new Union([new TNamedObject($class_storage->name)]); $this->project_analyzer->getMethodMutations( - new \Psalm\Internal\MethodIdentifier($method_id->fq_class_name, '__construct'), + new MethodIdentifier($method_id->fq_class_name, '__construct'), $this_context, $this->getRootFilePath(), $this->getRootFileName() ); - $this_context->vars_in_scope['$this'] = new Type\Union([new Type\Atomic\TNamedObject($class_storage->name)]); + $this_context->vars_in_scope['$this'] = new Union([new TNamedObject($class_storage->name)]); // check the actual method $this->project_analyzer->getMethodMutations( diff --git a/examples/plugins/FunctionCasingChecker.php b/examples/plugins/FunctionCasingChecker.php index cf974813f0a..6323251a8c6 100644 --- a/examples/plugins/FunctionCasingChecker.php +++ b/examples/plugins/FunctionCasingChecker.php @@ -12,6 +12,10 @@ use Psalm\Plugin\EventHandler\AfterMethodCallAnalysisInterface; use Psalm\Plugin\EventHandler\Event\AfterFunctionCallAnalysisEvent; use Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent; +use Psalm\Internal\Analyzer\StatementsAnalyzer; +use Psalm\Internal\Analyzer\FunctionLike\ReturnTypeAnalyzer; +use Psalm\Internal\MethodIdentifier; + use function explode; use function strtolower; use function end; @@ -33,7 +37,7 @@ public static function afterMethodCallAnalysis(AfterMethodCallAnalysisEvent $eve try { /** @psalm-suppress ArgumentTypeCoercion */ - $method_id = new \Psalm\Internal\MethodIdentifier(...explode('::', $declaring_method_id)); + $method_id = new MethodIdentifier(...explode('::', $declaring_method_id)); $function_storage = $codebase->methods->getStorage($method_id); if ($function_storage->cased_name === '__call') { @@ -76,7 +80,7 @@ public static function afterFunctionCallAnalysis(AfterFunctionCallAnalysisEvent try { $function_storage = $codebase->functions->getStorage( - $statements_source instanceof \Psalm\Internal\Analyzer\StatementsAnalyzer + $statements_source instanceof StatementsAnalyzer ? $statements_source : null, strtolower($function_id) diff --git a/examples/plugins/composer-based/echo-checker/EchoChecker.php b/examples/plugins/composer-based/echo-checker/EchoChecker.php index e833d7c81bf..2bb8908d281 100644 --- a/examples/plugins/composer-based/echo-checker/EchoChecker.php +++ b/examples/plugins/composer-based/echo-checker/EchoChecker.php @@ -8,6 +8,9 @@ use Psalm\Issue\ArgumentTypeCoercion; use Psalm\Plugin\EventHandler\AfterStatementAnalysisInterface; use Psalm\Plugin\EventHandler\Event\AfterStatementAnalysisEvent; +use Psalm\Type\Atomic\TString; +use Psalm\Type\Atomic\TLiteralString; +use Psalm\Type\Atomic\THtmlEscapedString; class EchoChecker implements AfterStatementAnalysisInterface { @@ -43,9 +46,9 @@ public static function afterStatementAnalysis(AfterStatementAnalysisEvent $event $types = $expr_type->getAtomicTypes(); foreach ($types as $type) { - if ($type instanceof \Psalm\Type\Atomic\TString - && !$type instanceof \Psalm\Type\Atomic\TLiteralString - && !$type instanceof \Psalm\Type\Atomic\THtmlEscapedString + if ($type instanceof TString + && !$type instanceof TLiteralString + && !$type instanceof THtmlEscapedString ) { if (IssueBuffer::accepts( new ArgumentTypeCoercion( diff --git a/phpcs.xml b/phpcs.xml index 3e7e43b964d..627943d181d 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -23,7 +23,6 @@ src/ tests/ - src/Psalm/Internal/Visitor/SimpleNameResolver.php src/Psalm/Internal/Stubs/ tests/fixtures/ @@ -73,6 +72,44 @@ * STANDARD: SlevomatCodingStandard * ************************************************************************************************************** --> + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/psalm-baseline.xml b/psalm-baseline.xml index cfc4635fb4a..9cb292cad54 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + $comment_block->tags['variablesfrom'][0] @@ -177,7 +177,7 @@ - Type\Atomic\TEmpty::class + TEmpty::class @@ -226,7 +226,7 @@ - Type\Atomic\TMixed|Type\Atomic\TTemplateParam|Type\Atomic\TEmpty + TMixed|TTemplateParam|TEmpty new TEmpty new TEmpty @@ -260,8 +260,8 @@ - new Type\Atomic\TEmpty - new Type\Atomic\TEmpty + new TEmpty + new TEmpty @@ -281,8 +281,8 @@ - new Type\Atomic\TEmpty() - new Type\Atomic\TEmpty() + new TEmpty() + new TEmpty() Type::getEmpty() @@ -395,8 +395,8 @@ - new Type\Atomic\TEmpty() - new Type\Atomic\TEmpty() + new TEmpty() + new TEmpty() diff --git a/src/Psalm/Aliases.php b/src/Psalm/Aliases.php index 2dbb4ca6bf7..1b7dd23321f 100644 --- a/src/Psalm/Aliases.php +++ b/src/Psalm/Aliases.php @@ -1,4 +1,5 @@ selection_start = $this->file_start; $this->selection_end = $this->file_end + 1; - $project_analyzer = Internal\Analyzer\ProjectAnalyzer::getInstance(); + $project_analyzer = ProjectAnalyzer::getInstance(); $codebase = $project_analyzer->getCodebase(); diff --git a/src/Psalm/CodeLocation/DocblockTypeLocation.php b/src/Psalm/CodeLocation/DocblockTypeLocation.php index 65e6398b3e4..224b740aac6 100644 --- a/src/Psalm/CodeLocation/DocblockTypeLocation.php +++ b/src/Psalm/CodeLocation/DocblockTypeLocation.php @@ -1,4 +1,5 @@ + * @var array */ private static $stubbed_constants = []; @@ -328,7 +341,7 @@ public function __construct( self::$stubbed_constants = []; - $reflection = new Internal\Codebase\Reflection($providers->classlike_storage_provider, $this); + $reflection = new Reflection($providers->classlike_storage_provider, $this); $this->scanner = new Scanner( $this, @@ -396,7 +409,7 @@ public function reloadFiles(ProjectAnalyzer $project_analyzer, array $candidate_ $this->file_reference_provider->loadReferenceCache(false); - Internal\Analyzer\FunctionLikeAnalyzer::clearCache(); + FunctionLikeAnalyzer::clearCache(); if (!$this->statements_provider->parser_cache_provider) { $diff_files = $candidate_files; @@ -552,7 +565,7 @@ public function exhumeClassLikeStorage(string $fq_classlike_name, string $file_p } } - public static function getPsalmTypeFromReflection(?ReflectionType $type): Type\Union + public static function getPsalmTypeFromReflection(?ReflectionType $type): Union { return Reflection::getPsalmTypeFromReflectionType($type); } @@ -619,7 +632,7 @@ public function findReferencesToClassLike(string $fq_class_name): array return $locations; } - public function getClosureStorage(string $file_path, string $closure_id): Storage\FunctionStorage + public function getClosureStorage(string $file_path, string $closure_id): FunctionStorage { $file_storage = $this->file_storage_provider->get($file_path); @@ -633,18 +646,18 @@ public function getClosureStorage(string $file_path, string $closure_id): Storag ); } - public function addGlobalConstantType(string $const_id, Type\Union $type): void + public function addGlobalConstantType(string $const_id, Union $type): void { self::$stubbed_constants[$const_id] = $type; } - public function getStubbedConstantType(string $const_id): ?Type\Union + public function getStubbedConstantType(string $const_id): ?Union { return self::$stubbed_constants[$const_id] ?? null; } /** - * @return array + * @return array */ public function getAllStubbedConstants(): array { @@ -785,7 +798,7 @@ public function traitHasCorrectCase(string $fq_trait_name): bool * * @param non-empty-string $function_id * - * @return Storage\FunctionStorage|Storage\MethodStorage + * @return FunctionStorage|MethodStorage */ public function getFunctionLikeStorage( StatementsAnalyzer $statements_analyzer, @@ -824,7 +837,7 @@ public function methodExists( bool $is_used = true ): bool { return $this->methods->methodExists( - Internal\MethodIdentifier::wrap($method_id), + MethodIdentifier::wrap($method_id), is_string($calling_method_id) ? strtolower($calling_method_id) : strtolower((string) $calling_method_id), $code_location, null, @@ -841,7 +854,7 @@ public function methodExists( */ public function getMethodParams($method_id): array { - return $this->methods->getMethodParams(Internal\MethodIdentifier::wrap($method_id)); + return $this->methods->getMethodParams(MethodIdentifier::wrap($method_id)); } /** @@ -850,7 +863,7 @@ public function getMethodParams($method_id): array */ public function isVariadic($method_id): bool { - return $this->methods->isVariadic(Internal\MethodIdentifier::wrap($method_id)); + return $this->methods->isVariadic(MethodIdentifier::wrap($method_id)); } /** @@ -858,10 +871,10 @@ public function isVariadic($method_id): bool * @param list $call_args * */ - public function getMethodReturnType($method_id, ?string &$self_class, array $call_args = []): ?Type\Union + public function getMethodReturnType($method_id, ?string &$self_class, array $call_args = []): ?Union { return $this->methods->getMethodReturnType( - Internal\MethodIdentifier::wrap($method_id), + MethodIdentifier::wrap($method_id), $self_class, null, $call_args @@ -874,7 +887,7 @@ public function getMethodReturnType($method_id, ?string &$self_class, array $cal */ public function getMethodReturnsByRef($method_id): bool { - return $this->methods->getMethodReturnsByRef(Internal\MethodIdentifier::wrap($method_id)); + return $this->methods->getMethodReturnsByRef(MethodIdentifier::wrap($method_id)); } /** @@ -887,7 +900,7 @@ public function getMethodReturnTypeLocation( CodeLocation &$defined_location = null ): ?CodeLocation { return $this->methods->getMethodReturnTypeLocation( - Internal\MethodIdentifier::wrap($method_id), + MethodIdentifier::wrap($method_id), $defined_location ); } @@ -898,7 +911,7 @@ public function getMethodReturnTypeLocation( */ public function getDeclaringMethodId($method_id): ?string { - $new_method_id = $this->methods->getDeclaringMethodId(Internal\MethodIdentifier::wrap($method_id)); + $new_method_id = $this->methods->getDeclaringMethodId(MethodIdentifier::wrap($method_id)); return $new_method_id ? (string) $new_method_id : null; } @@ -911,7 +924,7 @@ public function getDeclaringMethodId($method_id): ?string */ public function getAppearingMethodId($method_id): ?string { - $new_method_id = $this->methods->getAppearingMethodId(Internal\MethodIdentifier::wrap($method_id)); + $new_method_id = $this->methods->getAppearingMethodId(MethodIdentifier::wrap($method_id)); return $new_method_id ? (string) $new_method_id : null; } @@ -919,11 +932,11 @@ public function getAppearingMethodId($method_id): ?string /** * @param string|MethodIdentifier $method_id * - * @return array + * @return array */ public function getOverriddenMethodIds($method_id): array { - return $this->methods->getOverriddenMethodIds(Internal\MethodIdentifier::wrap($method_id)); + return $this->methods->getOverriddenMethodIds(MethodIdentifier::wrap($method_id)); } /** @@ -932,7 +945,7 @@ public function getOverriddenMethodIds($method_id): array */ public function getCasedMethodId($method_id): string { - return $this->methods->getCasedMethodId(Internal\MethodIdentifier::wrap($method_id)); + return $this->methods->getCasedMethodId(MethodIdentifier::wrap($method_id)); } public function invalidateInformationForFile(string $file_path): void @@ -1124,7 +1137,7 @@ public function getSymbolLocation(string $file_path, string $symbol): ?CodeLocat $file_contents = $this->getFileContents($file_path); - return new CodeLocation\Raw( + return new Raw( $file_contents, $file_path, $this->config->shortenFileName($file_path), @@ -1475,7 +1488,7 @@ public function getCompletionDataAtPosition(string $file_path, Position $positio return null; } - public function getTypeContextAtPosition(string $file_path, Position $position): ?Type\Union + public function getTypeContextAtPosition(string $file_path, Position $position): ?Union { $file_contents = $this->getFileContents($file_path); $offset = $position->toOffset($file_contents); @@ -1510,7 +1523,7 @@ public function getCompletionItemsForClassishThing(string $type_string, string $ $type = Type::parseString($type_string); foreach ($type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TNamedObject) { + if ($atomic_type instanceof TNamedObject) { try { $class_storage = $this->classlike_storage_provider->get($atomic_type->value); @@ -1750,11 +1763,11 @@ public function getCompletionItemsForPartialSymbol( /** * @return list */ - public function getCompletionItemsForType(Type\Union $type): array + public function getCompletionItemsForType(Union $type): array { $completion_items = []; foreach ($type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TBool) { + if ($atomic_type instanceof TBool) { $bools = (string) $atomic_type === 'bool' ? ['true', 'false'] : [(string) $atomic_type]; foreach ($bools as $property_name) { $completion_items[] = new CompletionItem( @@ -1767,7 +1780,7 @@ public function getCompletionItemsForType(Type\Union $type): array $property_name ); } - } elseif ($atomic_type instanceof Type\Atomic\TLiteralString) { + } elseif ($atomic_type instanceof TLiteralString) { $completion_items[] = new CompletionItem( $atomic_type->value, CompletionItemKind::VALUE, @@ -1777,7 +1790,7 @@ public function getCompletionItemsForType(Type\Union $type): array null, "'$atomic_type->value'" ); - } elseif ($atomic_type instanceof Type\Atomic\TLiteralInt) { + } elseif ($atomic_type instanceof TLiteralInt) { $completion_items[] = new CompletionItem( (string) $atomic_type->value, CompletionItemKind::VALUE, @@ -1787,7 +1800,7 @@ public function getCompletionItemsForType(Type\Union $type): array null, (string) $atomic_type->value ); - } elseif ($atomic_type instanceof Type\Atomic\TClassConstant) { + } elseif ($atomic_type instanceof TClassConstant) { $const = $atomic_type->fq_classlike_name . '::' . $atomic_type->const_name; $completion_items[] = new CompletionItem( $const, @@ -1812,7 +1825,7 @@ public function getCompletionItemsForArrayKeys( $completion_items = []; $type = Type::parseString($type_string); foreach ($type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_type instanceof TKeyedArray) { foreach ($atomic_type->properties as $property_name => $property) { $completion_items[] = new CompletionItem( (string) $property_name, @@ -1862,7 +1875,7 @@ public function removeTemporaryFileChanges(string $file_path): void * Checks if type is a subtype of other * * Given two types, checks if `$input_type` is a subtype of `$container_type`. - * If you consider `Type\Union` as a set of types, this will tell you + * If you consider `Union` as a set of types, this will tell you * if `$input_type` is fully contained in `$container_type`, * * $input_type ⊆ $container_type @@ -1871,8 +1884,8 @@ public function removeTemporaryFileChanges(string $file_path): void * should be a subset of the function parameter type. */ public function isTypeContainedByType( - Type\Union $input_type, - Type\Union $container_type + Union $input_type, + Union $container_type ): bool { return UnionTypeComparator::isContainedBy($this, $input_type, $container_type); } @@ -1881,7 +1894,7 @@ public function isTypeContainedByType( * Checks if type has any part that is a subtype of other * * Given two types, checks if *any part* of `$input_type` is a subtype of `$container_type`. - * If you consider `Type\Union` as a set of types, this will tell you if intersection + * If you consider `Union` as a set of types, this will tell you if intersection * of `$input_type` with `$container_type` is not empty. * * $input_type ∩ $container_type ≠ ∅ , e.g. they are not disjoint. @@ -1891,8 +1904,8 @@ public function isTypeContainedByType( * not a subtype of the required type. */ public function canTypeBeContainedByType( - Type\Union $input_type, - Type\Union $container_type + Union $input_type, + Union $container_type ): bool { return UnionTypeComparator::canBeContainedBy($this, $input_type, $container_type); } @@ -1909,9 +1922,9 @@ public function canTypeBeContainedByType( * // returns [Union(TInt), Union(TString)] * ``` * - * @return array{Type\Union,Type\Union} + * @return array{Union, Union} */ - public function getKeyValueParamsForTraversableObject(Type\Atomic $type): array + public function getKeyValueParamsForTraversableObject(Atomic $type): array { $key_type = null; $value_type = null; @@ -1943,7 +1956,7 @@ public function queueClassLikeForScanning( * @psalm-suppress PossiblyUnusedMethod */ public function addTaintSource( - Type\Union $expr_type, + Union $expr_type, string $taint_id, array $taints = TaintKindGroup::ALL_INPUT, ?CodeLocation $code_location = null diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index cca3933511b..f0e4ff28299 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -10,6 +10,7 @@ use InvalidArgumentException; use LogicException; use OutOfBoundsException; +use Psalm\CodeLocation\Raw; use Psalm\Config\IssueHandler; use Psalm\Config\ProjectFileFilter; use Psalm\Config\TaintAnalysisFileFilter; @@ -31,6 +32,7 @@ use Psalm\Issue\MethodIssue; use Psalm\Issue\PropertyIssue; use Psalm\Issue\VariableIssue; +use Psalm\Plugin\PluginEntryPointInterface; use Psalm\Progress\Progress; use Psalm\Progress\VoidProgress; use SimpleXMLElement; @@ -806,7 +808,7 @@ private static function processConfigDeprecations( $config->config_issues[] = new ConfigIssue( 'Attribute "' . $attribute->name . '" is deprecated ' . 'and is going to be removed in the next major version', - new CodeLocation\Raw( + new Raw( $file_contents, $config_path, basename($config_path), @@ -835,7 +837,7 @@ private static function processConfigDeprecations( $config->config_issues[] = new ConfigIssue( 'Element "' . $deprecated_element . '" is deprecated ' . 'and is going to be removed in the next major version', - new CodeLocation\Raw( + new Raw( $file_contents, $config_path, basename($config_path), @@ -996,7 +998,7 @@ private static function fromXmlAndPaths( $attribute_text = (int) $config_xml['errorLevel']; if (!in_array($attribute_text, [1, 2, 3, 4, 5, 6, 7, 8], true)) { - throw new Exception\ConfigException( + throw new ConfigException( 'Invalid error level ' . $config_xml['errorLevel'] ); } @@ -1129,7 +1131,7 @@ private static function fromXmlAndPaths( $file_path = realpath($stub_file_name); if (!$file_path) { - throw new Exception\ConfigException( + throw new ConfigException( 'Cannot resolve stubfile path ' . rtrim($config->base_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR @@ -1253,7 +1255,7 @@ private function loadFileExtensions(SimpleXMLElement $extensions): void $path = $this->base_dir . (string)$extension['scanner']; if (!file_exists($path)) { - throw new Exception\ConfigException('Error parsing config: cannot find file ' . $path); + throw new ConfigException('Error parsing config: cannot find file ' . $path); } $this->filetype_scanner_paths[$extension_name] = $path; @@ -1263,7 +1265,7 @@ private function loadFileExtensions(SimpleXMLElement $extensions): void $path = $this->base_dir . (string)$extension['checker']; if (!file_exists($path)) { - throw new Exception\ConfigException('Error parsing config: cannot find file ' . $path); + throw new ConfigException('Error parsing config: cannot find file ' . $path); } $this->filetype_analyzer_paths[$extension_name] = $path; @@ -1329,7 +1331,7 @@ public function initializePlugins(ProjectAnalyzer $project_analyzer): void /** * @psalm-suppress InvalidStringClass * - * @var Plugin\PluginEntryPointInterface + * @var PluginEntryPointInterface */ $plugin_object = new $plugin_class_name; $plugin_object($socket, $plugin_config); diff --git a/src/Psalm/Config/Creator.php b/src/Psalm/Config/Creator.php index 7bfbda78f88..36760726e1c 100644 --- a/src/Psalm/Config/Creator.php +++ b/src/Psalm/Config/Creator.php @@ -1,4 +1,5 @@ directory as $directory) { $config['directory'][] = [ 'name' => (string) $directory['name'], - 'ignoreTypeStats' => strtolower( - isset($directory['ignoreTypeStats']) ? (string) $directory['ignoreTypeStats'] : '' - ) === 'true', - - 'resolveSymlinks' => strtolower( - isset($directory['resolveSymlinks']) ? (string) $directory['resolveSymlinks'] : '' - ) === 'true', - 'useStrictTypes' => strtolower( - isset($directory['useStrictTypes']) ? (string) $directory['useStrictTypes'] : '' - ) === 'true', + 'ignoreTypeStats' => strtolower((string) ($directory['ignoreTypeStats'] ?? '')) === 'true', + 'resolveSymlinks' => strtolower((string) ($directory['resolveSymlinks'] ?? '')) === 'true', + 'useStrictTypes' => strtolower((string) ($directory['useStrictTypes'] ?? '')) === 'true', ]; } } diff --git a/src/Psalm/Config/IssueHandler.php b/src/Psalm/Config/IssueHandler.php index e73b44c8019..cdc2c288289 100644 --- a/src/Psalm/Config/IssueHandler.php +++ b/src/Psalm/Config/IssueHandler.php @@ -1,4 +1,5 @@ + * @var array */ public $vars_in_scope = []; @@ -203,7 +206,7 @@ class Context public $initialized_methods; /** - * @var array + * @var array */ public $constants = []; @@ -452,9 +455,9 @@ public function update( } /** - * @param array $new_vars_in_scope + * @param array $new_vars_in_scope * - * @return array + * @return array */ public function getRedefinedVars(array $new_vars_in_scope, bool $include_new_vars = false): array { @@ -664,7 +667,7 @@ public function removeDescendents( } foreach ($type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\DependentType + if ($atomic_type instanceof DependentType && $atomic_type->getVarId() === $remove_var_id ) { $type->addType($atomic_type->getReplacement()); @@ -771,8 +774,8 @@ public function getScopeSummary(): string public function defineGlobals(): void { $globals = [ - '$argv' => new Type\Union([ - new Type\Atomic\TArray([Type::getInt(), Type::getString()]), + '$argv' => new Union([ + new TArray([Type::getInt(), Type::getString()]), ]), '$argc' => Type::getInt(), ]; diff --git a/src/Psalm/DocComment.php b/src/Psalm/DocComment.php index 1060f1ee8a2..ae3cd6ca32a 100644 --- a/src/Psalm/DocComment.php +++ b/src/Psalm/DocComment.php @@ -1,4 +1,5 @@ }>> * - * @throws Exception\ConfigException + * @throws ConfigException */ public static function read(FileProvider $fileProvider, string $baselineFile): array { if (!$fileProvider->fileExists($baselineFile)) { - throw new Exception\ConfigException("{$baselineFile} does not exist or is not readable"); + throw new ConfigException("{$baselineFile} does not exist or is not readable"); } $xmlSource = $fileProvider->getContents($baselineFile); if ($xmlSource === '') { - throw new Exception\ConfigException('Baseline file is empty'); + throw new ConfigException('Baseline file is empty'); } $baselineDoc = new DOMDocument(); @@ -94,7 +96,7 @@ public static function read(FileProvider $fileProvider, string $baselineFile): a $filesElement = $baselineDoc->getElementsByTagName('files'); if ($filesElement->length === 0) { - throw new Exception\ConfigException('Baseline file does not contain '); + throw new ConfigException('Baseline file does not contain '); } $files = []; @@ -136,7 +138,7 @@ public static function read(FileProvider $fileProvider, string $baselineFile): a * * @return array}>> * - * @throws Exception\ConfigException + * @throws ConfigException */ public static function update( FileProvider $fileProvider, diff --git a/src/Psalm/Exception/CircularReferenceException.php b/src/Psalm/Exception/CircularReferenceException.php index cd055ed1cef..2056b1a3068 100644 --- a/src/Psalm/Exception/CircularReferenceException.php +++ b/src/Psalm/Exception/CircularReferenceException.php @@ -1,4 +1,5 @@ + * @var array */ public $inferred_property_types = []; @@ -286,11 +293,11 @@ public function analyze( if (($storage->templatedMixins || $storage->namedMixins) && $storage->mixin_declaring_fqcln === $storage->name) { - /** @var non-empty-array $mixins */ + /** @var non-empty-array $mixins */ $mixins = array_merge($storage->templatedMixins, $storage->namedMixins); - $union = new Type\Union($mixins); + $union = new Union($mixins); - $static_self = new Type\Atomic\TNamedObject($storage->name); + $static_self = new TNamedObject($storage->name); $static_self->was_static = true; $union = TypeExpander::expandUnion( @@ -859,7 +866,7 @@ public static function addContextProperties( $property_class_storage, $storage, null, - new Type\Atomic\TNamedObject($fq_class_name), + new TNamedObject($fq_class_name), true ); @@ -869,14 +876,14 @@ public static function addContextProperties( $fq_class_name ); - if (!$this_object_type instanceof Type\Atomic\TGenericObject) { + if (!$this_object_type instanceof TGenericObject) { $type_params = []; foreach ($class_template_params as $type_map) { $type_params[] = clone array_values($type_map)[0]; } - $this_object_type = new Type\Atomic\TGenericObject($this_object_type->value, $type_params); + $this_object_type = new TGenericObject($this_object_type->value, $type_params); } $fleshed_out_type = AtomicPropertyFetchAnalyzer::localizePropertyType( @@ -1221,10 +1228,10 @@ function (FunctionLikeParameter $param): PhpParser\Node\Arg { $method_context->collect_nonprivate_initializations = !$uninitialized_private_properties; $method_context->self = $fq_class_name; - $this_atomic_object_type = new Type\Atomic\TNamedObject($fq_class_name); + $this_atomic_object_type = new TNamedObject($fq_class_name); $this_atomic_object_type->was_static = !$storage->final; - $method_context->vars_in_scope['$this'] = new Type\Union([$this_atomic_object_type]); + $method_context->vars_in_scope['$this'] = new Union([$this_atomic_object_type]); $method_context->vars_possibly_in_scope['$this'] = true; $method_context->calling_method_id = strtolower($fq_class_name) . '::__construct'; @@ -1292,7 +1299,7 @@ function (FunctionLikeParameter $param): PhpParser\Node\Arg { ); } elseif (!$property_storage->has_default) { if (isset($this->inferred_property_types[$property_name])) { - $this->inferred_property_types[$property_name]->addType(new Type\Atomic\TNull()); + $this->inferred_property_types[$property_name]->addType(new TNull()); $this->inferred_property_types[$property_name]->setFromDocblock(); } } @@ -1541,7 +1548,7 @@ private function checkForMissingPropertyType( } if ($suggested_type && !$property_storage->has_default && $property_storage->is_static) { - $suggested_type->addType(new Type\Atomic\TNull()); + $suggested_type->addType(new TNull()); } if ($suggested_type && !$suggested_type->isNull()) { @@ -1588,7 +1595,7 @@ private function checkForMissingPropertyType( private static function addOrUpdatePropertyType( ProjectAnalyzer $project_analyzer, PhpParser\Node\Stmt\Property $property, - Type\Union $inferred_type, + Union $inferred_type, StatementsSource $source, bool $docblock_only = false ): void { @@ -1813,15 +1820,15 @@ private function analyzeClassMethod( private static function getThisObjectType( ClassLikeStorage $class_storage, string $original_fq_classlike_name - ): Type\Atomic\TNamedObject { + ): TNamedObject { if ($class_storage->template_types) { $template_params = []; foreach ($class_storage->template_types as $param_name => $template_map) { $key = array_keys($template_map)[0]; - $template_params[] = new Type\Union([ - new Type\Atomic\TTemplateParam( + $template_params[] = new Union([ + new TTemplateParam( $param_name, reset($template_map), $key @@ -1829,13 +1836,13 @@ private static function getThisObjectType( ]); } - return new Type\Atomic\TGenericObject( + return new TGenericObject( $original_fq_classlike_name, $template_params ); } - return new Type\Atomic\TNamedObject($original_fq_classlike_name); + return new TNamedObject($original_fq_classlike_name); } public static function analyzeClassMethodReturnType( @@ -1927,7 +1934,7 @@ public static function analyzeClassMethodReturnType( $interface_method_id ); - FunctionLike\ReturnTypeAnalyzer::verifyReturnType( + ReturnTypeAnalyzer::verifyReturnType( $stmt, $stmt->getStmts() ?: [], $source, @@ -1954,7 +1961,7 @@ function ($method_id) { } - FunctionLike\ReturnTypeAnalyzer::verifyReturnType( + ReturnTypeAnalyzer::verifyReturnType( $stmt, $stmt->getStmts() ?: [], $source, @@ -2042,7 +2049,7 @@ private function checkTemplateParams( && !$parent_storage->template_covariants[$i] ) { foreach ($extended_type->getAtomicTypes() as $t) { - if ($t instanceof Type\Atomic\TTemplateParam + if ($t instanceof TTemplateParam && $storage->template_types && $storage->template_covariants && ($local_offset @@ -2064,7 +2071,7 @@ private function checkTemplateParams( if ($parent_storage->enforce_template_inheritance) { foreach ($extended_type->getAtomicTypes() as $t) { - if (!$t instanceof Type\Atomic\TTemplateParam + if (!$t instanceof TTemplateParam || !isset($storage->template_types[$t->param_name]) ) { IssueBuffer::maybeAdd( diff --git a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php index d7728d32114..0b4934e8de0 100644 --- a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php @@ -1,4 +1,5 @@ >|null + * @return array>|null */ public function getTemplateTypeMap(): ?array { @@ -480,7 +482,7 @@ public function isStatic(): bool * @param mixed $value * */ - public static function getTypeFromValue($value): Type\Union + public static function getTypeFromValue($value): Union { switch (gettype($value)) { case 'boolean': diff --git a/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php b/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php index bf3a6084dbc..dff50564c9b 100644 --- a/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php +++ b/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php @@ -1,4 +1,5 @@ self); $this_atomic->was_static = true; - $use_context->vars_in_scope['$this'] = new Type\Union([$this_atomic]); + $use_context->vars_in_scope['$this'] = new Union([$this_atomic]); } } diff --git a/src/Psalm/Internal/Analyzer/CommentAnalyzer.php b/src/Psalm/Internal/Analyzer/CommentAnalyzer.php index d57cf17817a..de576c0001f 100644 --- a/src/Psalm/Internal/Analyzer/CommentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/CommentAnalyzer.php @@ -1,4 +1,5 @@ \[\]\-\{\}:|?\\\\]*|\$[a-zA-Z_0-9_]+)'; /** - * @param array>|null $template_type_map + * @param array>|null $template_type_map * @param array $type_aliases * * @throws DocblockParseException if there was a problem parsing the docblock @@ -63,7 +64,7 @@ public static function getTypeFromComment( } /** - * @param array>|null $template_type_map + * @param array>|null $template_type_map * @param array $type_aliases * * @return list diff --git a/src/Psalm/Internal/Analyzer/FileAnalyzer.php b/src/Psalm/Internal/Analyzer/FileAnalyzer.php index 0d26436d40a..61a220f1090 100644 --- a/src/Psalm/Internal/Analyzer/FileAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FileAnalyzer.php @@ -1,4 +1,5 @@ >|null + * @return array>|null */ public function getTemplateTypeMap(): ?array { @@ -666,7 +668,7 @@ public function getNodeTypeProvider(): NodeTypeProvider return $this->node_data; } - public function getReturnType(): ?Type\Union + public function getReturnType(): ?Union { return $this->return_type; } diff --git a/src/Psalm/Internal/Analyzer/FunctionAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionAnalyzer.php index a03542503d5..c11aa47c376 100644 --- a/src/Psalm/Internal/Analyzer/FunctionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionAnalyzer.php @@ -1,4 +1,5 @@ isVoid()) { - $atomic_null = new Type\Atomic\TNull(); + $atomic_null = new TNull(); $atomic_null->from_docblock = true; - $inferred_return_type_parts[] = new Type\Union([$atomic_null]); + $inferred_return_type_parts[] = new Union([$atomic_null]); break; } } @@ -250,7 +254,7 @@ static function (Union $union_type): bool { : Type::getVoid(); if ($function_always_exits) { - $inferred_return_type = new Type\Union([new Type\Atomic\TNever]); + $inferred_return_type = new Union([new TNever]); } $inferred_yield_type = $inferred_yield_types @@ -290,7 +294,7 @@ static function (Union $union_type): bool { && !$inferred_yield_type && !$inferred_return_type->isVoid() ) { - $inferred_return_type = new Type\Union([new Type\Atomic\TNamedObject('Generator')]); + $inferred_return_type = new Union([new TNamedObject('Generator')]); } if ($is_to_string) { @@ -759,7 +763,7 @@ public static function checkReturnType( if (!$storage->signature_return_type || $storage->signature_return_type === $storage->return_type) { foreach ($storage->return_type->getAtomicTypes() as $type) { - if ($type instanceof Type\Atomic\TNamedObject + if ($type instanceof TNamedObject && 'parent' === $type->value && null === $parent_class ) { @@ -847,7 +851,7 @@ public static function checkReturnType( $classlike_storage, $codebase->classlike_storage_provider->get($context->self), strtolower($function->name->name), - new Type\Atomic\TNamedObject($context->self), + new TNamedObject($context->self), true ); @@ -917,7 +921,7 @@ public static function checkReturnType( private static function addOrUpdateReturnType( FunctionLike $function, ProjectAnalyzer $project_analyzer, - Type\Union $inferred_return_type, + Union $inferred_return_type, StatementsSource $source, bool $docblock_only = false, ?FunctionLikeStorage $function_like_storage = null diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php index cd1b239d313..2ce6434c00f 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php @@ -1,4 +1,5 @@ $stmts - * @param list $yield_types + * @param list $yield_types * - * @return list a list of return types + * @return list a list of return types * * @psalm-suppress ComplexMethod to be refactored * @@ -235,9 +242,9 @@ public static function getReturnTypes( } /** - * @param list $return_types - * @param non-empty-list $yield_types - * @return non-empty-list + * @param list $return_types + * @param non-empty-list $yield_types + * @return non-empty-list */ private static function processYieldTypes( Codebase $codebase, @@ -250,21 +257,21 @@ private static function processYieldTypes( $yield_type = Type::combineUnionTypeArray($yield_types, null); foreach ($yield_type->getAtomicTypes() as $type) { - if ($type instanceof Type\Atomic\TKeyedArray) { + if ($type instanceof TKeyedArray) { $type = $type->getGenericArrayType(); } - if ($type instanceof Type\Atomic\TList) { - $type = new Type\Atomic\TArray([Type::getInt(), $type->type_param]); + if ($type instanceof TList) { + $type = new TArray([Type::getInt(), $type->type_param]); } - if ($type instanceof Type\Atomic\TArray) { + if ($type instanceof TArray) { [$key_type_param, $value_type_param] = $type->type_params; $key_type = Type::combineUnionTypes(clone $key_type_param, $key_type); $value_type = Type::combineUnionTypes(clone $value_type_param, $value_type); - } elseif ($type instanceof Type\Atomic\TIterable - || $type instanceof Type\Atomic\TNamedObject + } elseif ($type instanceof TIterable + || $type instanceof TNamedObject ) { ForeachAnalyzer::getKeyValueParamsForTraversableObject( $type, @@ -276,8 +283,8 @@ private static function processYieldTypes( } return [ - new Type\Union([ - new Atomic\TGenericObject( + new Union([ + new TGenericObject( 'Generator', [ $key_type ?? Type::getMixed(), @@ -291,7 +298,7 @@ private static function processYieldTypes( } /** - * @return list + * @return list */ protected static function getYieldTypeFromExpression( PhpParser\Node\Expr $stmt, @@ -307,7 +314,7 @@ protected static function getYieldTypeFromExpression( if ($stmt->value && $value_type = $nodes->getType($stmt->value) ) { - $generator_type = new Atomic\TGenericObject( + $generator_type = new TGenericObject( 'Generator', [ $key_type ? clone $key_type : Type::getInt(), @@ -317,7 +324,7 @@ protected static function getYieldTypeFromExpression( ] ); - return [new Type\Union([$generator_type])]; + return [new Union([$generator_type])]; } return [Type::getMixed()]; diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 1c64cfa871e..6fd420900b0 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -1,4 +1,5 @@ + * @var ?array */ protected $return_vars_in_scope = []; @@ -101,7 +108,7 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer protected $return_vars_possibly_in_scope = []; /** - * @var Type\Union|null + * @var Union|null */ private $local_return_type; @@ -600,7 +607,7 @@ public function analyze( if ($function_type = $statements_analyzer->node_data->getType($this->function)) { /** - * @var Type\Atomic\TClosure + * @var TClosure */ $closure_atomic = $function_type->getSingleAtomic(); @@ -658,8 +665,8 @@ public function analyze( true ) )) { - $input_type = new Type\Union([new TNamedObject($expected_exception)]); - $container_type = new Type\Union([new TNamedObject('Exception'), new TNamedObject('Throwable')]); + $input_type = new Union([new TNamedObject($expected_exception)]); + $container_type = new Union([new TNamedObject('Exception'), new TNamedObject('Throwable')]); if (!UnionTypeComparator::isContainedBy($codebase, $input_type, $container_type)) { IssueBuffer::maybeAdd( @@ -1032,12 +1039,12 @@ private function processParams( if ($function_param->is_variadic) { if ($storage->allow_named_arg_calls) { - $var_type = new Type\Union([ - new Type\Atomic\TArray([Type::getArrayKey(), $param_type]), + $var_type = new Union([ + new TArray([Type::getArrayKey(), $param_type]), ]); } else { - $var_type = new Type\Union([ - new Type\Atomic\TList($param_type), + $var_type = new Union([ + new TList($param_type), ]); } } @@ -1389,7 +1396,7 @@ private function alterParams( public function verifyReturnType( array $function_stmts, StatementsAnalyzer $statements_analyzer, - ?Type\Union $return_type = null, + ?Union $return_type = null, ?string $fq_class_name = null, ?CodeLocation $return_type_location = null, bool $did_explicitly_return = false, @@ -1413,7 +1420,7 @@ public function verifyReturnType( public function addOrUpdateParamType( ProjectAnalyzer $project_analyzer, string $param_name, - Type\Union $inferred_return_type, + Union $inferred_return_type, bool $docblock_only = false ): void { $manipulator = FunctionDocblockManipulator::getForFunction( @@ -1641,7 +1648,7 @@ public function getAliasedClassesFlippedReplaceable(): array } /** - * @return array>|null + * @return array>|null */ public function getTemplateTypeMap(): ?array { @@ -1711,7 +1718,7 @@ public static function clearCache(): void self::$no_effects_hashes = []; } - public function getLocalReturnType(Type\Union $storage_return_type, bool $final = false): Type\Union + public function getLocalReturnType(Union $storage_return_type, bool $final = false): Union { if ($this->local_return_type) { return $this->local_return_type; @@ -1785,8 +1792,8 @@ private function getFunctionInformation( foreach ($appearing_class_storage->template_types as $param_name => $template_map) { $key = array_keys($template_map)[0]; - $template_params[] = new Type\Union([ - new Type\Atomic\TTemplateParam( + $template_params[] = new Union([ + new TTemplateParam( $param_name, reset($template_map), $key @@ -1794,7 +1801,7 @@ private function getFunctionInformation( ]); } - $this_object_type = new Type\Atomic\TGenericObject( + $this_object_type = new TGenericObject( $context->self, $template_params ); @@ -1804,7 +1811,7 @@ private function getFunctionInformation( $this_object_type->was_static = !$storage->final; } - $context->vars_in_scope['$this'] = new Type\Union([$this_object_type]); + $context->vars_in_scope['$this'] = new Union([$this_object_type]); if ($codebase->taint_flow_graph && $storage->specialize_call @@ -1931,7 +1938,7 @@ private function getFunctionInformation( $closure_return_type = Type::getMixed(); } - $closure_type = new Type\Atomic\TClosure( + $closure_type = new TClosure( 'Closure', $storage->params, $closure_return_type @@ -1944,7 +1951,7 @@ private function getFunctionInformation( $type_provider->setType( $this->function, - new Type\Union([ + new Union([ $closure_type, ]) ); diff --git a/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php b/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php index 2b17ceff514..0e9b11e4abb 100644 --- a/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php @@ -1,4 +1,5 @@ addType(new Type\Atomic\TNull); + $or_null_guide_param_signature_type->addType(new TNull); } if ($cased_guide_method_id === 'Serializable::unserialize') { @@ -525,7 +529,7 @@ private static function compareMethodSignatureParams( MethodStorage $guide_method_storage, MethodStorage $implementer_method_storage, FunctionLikeParameter $guide_param, - Type\Union $implementer_param_signature_type, + Union $implementer_param_signature_type, string $cased_guide_method_id, string $cased_implementer_method_id, CodeLocation $code_location, @@ -648,8 +652,8 @@ private static function compareMethodDocblockParams( MethodStorage $implementer_method_storage, string $cased_guide_method_id, string $cased_implementer_method_id, - Type\Union $guide_param_type, - Type\Union $implementer_param_type, + Union $guide_param_type, + Union $implementer_param_type, CodeLocation $code_location, array $suppressed_issues ): void { @@ -702,7 +706,7 @@ private static function compareMethodDocblockParams( } foreach ($implementer_method_storage_param_type->getAtomicTypes() as $k => $t) { - if ($t instanceof Type\Atomic\TTemplateParam + if ($t instanceof TTemplateParam && strpos($t->defining_class, 'fn-') === 0 ) { $implementer_method_storage_param_type->removeType($k); @@ -714,7 +718,7 @@ private static function compareMethodDocblockParams( } foreach ($guide_method_storage_param_type->getAtomicTypes() as $k => $t) { - if ($t instanceof Type\Atomic\TTemplateParam + if ($t instanceof TTemplateParam && strpos($t->defining_class, 'fn-') === 0 ) { $guide_method_storage_param_type->removeType($k); @@ -811,7 +815,7 @@ private static function compareMethodSignatureReturnTypes( ClassLikeStorage $implementer_classlike_storage, MethodStorage $guide_method_storage, MethodStorage $implementer_method_storage, - Type\Union $guide_signature_return_type, + Union $guide_signature_return_type, string $cased_guide_method_id, string $implementer_called_class_name, string $cased_implementer_method_id, @@ -899,8 +903,8 @@ private static function compareMethodDocblockReturnTypes( ClassLikeStorage $guide_classlike_storage, ClassLikeStorage $implementer_classlike_storage, MethodStorage $implementer_method_storage, - Type\Union $guide_return_type, - Type\Union $implementer_return_type, + Union $guide_return_type, + Union $implementer_return_type, string $cased_guide_method_id, string $implementer_called_class_name, ?MethodIdentifier $implementer_declaring_method_id, @@ -1024,12 +1028,12 @@ private static function compareMethodDocblockReturnTypes( } /** - * @param array> $template_extended_params + * @param array> $template_extended_params */ private static function transformTemplates( array $template_extended_params, string $base_class_name, - Type\Union $templated_type, + Union $templated_type, Codebase $codebase ): void { if (isset($template_extended_params[$base_class_name])) { diff --git a/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php b/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php index aa86c4d6d15..436e47bcd7f 100644 --- a/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php @@ -1,4 +1,5 @@ > + * @var array> */ protected static $public_namespace_constants = []; @@ -122,13 +124,13 @@ public function getNamespace(): string return $this->namespace_name; } - public function setConstType(string $const_name, Type\Union $const_type): void + public function setConstType(string $const_name, Union $const_type): void { self::$public_namespace_constants[$this->namespace_name][$const_name] = $const_type; } /** - * @return array + * @return array */ public static function getConstantsForNamespace(string $namespace_name, int $visibility): array { diff --git a/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php b/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php index a32260a7573..86a1160cc69 100644 --- a/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php @@ -1,4 +1,5 @@ config->getFileExtensions(); foreach ($this->config->getProjectDirectories() as $dir_name) { - $file_paths = $this->file_provider->getFilesInDir($dir_name, $file_extensions); + $file_paths = $this->file_provider->getFilesInDir( + $dir_name, + $file_extensions, + [$this->config, 'isInProjectDirs'] + ); foreach ($file_paths as $file_path) { if ($this->config->isInProjectDirs($file_path)) { @@ -318,7 +323,11 @@ public function __construct( } foreach ($this->config->getExtraDirectories() as $dir_name) { - $file_paths = $this->file_provider->getFilesInDir($dir_name, $file_extensions); + $file_paths = $this->file_provider->getFilesInDir( + $dir_name, + $file_extensions, + [$this->config, 'isInExtraDirs'] + ); foreach ($file_paths as $file_path) { if ($this->config->isInExtraDirs($file_path)) { @@ -610,11 +619,7 @@ public function check(string $base_dir, bool $is_diff = false): void && $this->project_cache_provider->canDiffFiles() ) { $deleted_files = $this->file_reference_provider->getDeletedReferencedFiles(); - $diff_files = $deleted_files; - - foreach ($this->config->getProjectDirectories() as $dir_name) { - $diff_files = array_merge($diff_files, $this->getDiffFilesInDir($dir_name, $this->config)); - } + $diff_files = array_merge($deleted_files, $this->getDiffFiles()); } $this->progress->write($this->generatePHPVersionMessage()); @@ -1052,8 +1057,13 @@ public function checkDir(string $dir_name): void private function checkDirWithConfig(string $dir_name, Config $config, bool $allow_non_project_files = false): void { $file_extensions = $config->getFileExtensions(); + $directory_filter = $allow_non_project_files ? null : [$this->config, 'isInProjectDirs']; - $file_paths = $this->file_provider->getFilesInDir($dir_name, $file_extensions); + $file_paths = $this->file_provider->getFilesInDir( + $dir_name, + $file_extensions, + $directory_filter + ); $files_to_scan = []; @@ -1079,10 +1089,8 @@ public function addExtraFile(string $file_path): void /** * @return list */ - protected function getDiffFilesInDir(string $dir_name, Config $config): array + protected function getDiffFiles(): array { - $file_extensions = $config->getFileExtensions(); - if (!$this->parser_cache_provider || !$this->project_cache_provider) { throw new UnexpectedValueException('Parser cache provider cannot be null here'); } @@ -1091,16 +1099,12 @@ protected function getDiffFilesInDir(string $dir_name, Config $config): array $last_run = $this->project_cache_provider->getLastRun(PSALM_VERSION); - $file_paths = $this->file_provider->getFilesInDir($dir_name, $file_extensions); - - foreach ($file_paths as $file_path) { - if ($config->isInProjectDirs($file_path)) { - if ($this->file_provider->getModifiedTime($file_path) >= $last_run - && $this->parser_cache_provider->loadExistingFileContentsFromCache($file_path) - !== $this->file_provider->getContents($file_path) - ) { - $diff_files[] = $file_path; - } + foreach ($this->project_files as $file_path) { + if ($this->file_provider->getModifiedTime($file_path) >= $last_run + && $this->parser_cache_provider->loadExistingFileContentsFromCache($file_path) + !== $this->file_provider->getContents($file_path) + ) { + $diff_files[] = $file_path; } } diff --git a/src/Psalm/Internal/Analyzer/SourceAnalyzer.php b/src/Psalm/Internal/Analyzer/SourceAnalyzer.php index f2c05f5003b..8be2d28a7ce 100644 --- a/src/Psalm/Internal/Analyzer/SourceAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/SourceAnalyzer.php @@ -1,11 +1,12 @@ >|null + * @return array>|null */ public function getTemplateTypeMap(): ?array { diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/DoAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/DoAnalyzer.php index e707bdabd28..aeeb964f90a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/DoAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/DoAnalyzer.php @@ -1,4 +1,5 @@ vars_in_scope = - Type\Reconciler::reconcileKeyedTypes( + Reconciler::reconcileKeyedTypes( $negated_while_types, [], $inner_loop_context->vars_in_scope, diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php index b9d3f85fb49..c45ca9b6d0f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php @@ -1,4 +1,5 @@ type_end && $var_comment->line_number ) { - $type_location = new CodeLocation\DocblockTypeLocation( + $type_location = new DocblockTypeLocation( $statements_analyzer, $var_comment->type_start, $var_comment->type_end, @@ -200,6 +223,8 @@ public static function analyze( $was_inside_general_use = $context->inside_general_use; $context->inside_general_use = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return false; } $context->inside_general_use = $was_inside_general_use; @@ -356,11 +381,11 @@ public static function checkIteratorType( StatementsAnalyzer $statements_analyzer, PhpParser\NodeAbstract $stmt, PhpParser\Node\Expr $expr, - Type\Union $iterator_type, + Union $iterator_type, Codebase $codebase, Context $context, - ?Type\Union &$key_type, - ?Type\Union &$value_type, + ?Union &$key_type, + ?Union &$value_type, bool &$always_non_empty_array ): ?bool { if ($iterator_type->isNull()) { @@ -405,12 +430,12 @@ public static function checkIteratorType( $raw_object_types = []; foreach ($iterator_type->getAtomicTypes() as $iterator_atomic_type) { - if ($iterator_atomic_type instanceof Type\Atomic\TTemplateParam) { + if ($iterator_atomic_type instanceof TTemplateParam) { $iterator_atomic_type = $iterator_atomic_type->as->getSingleAtomic(); } // if it's an empty array, we cannot iterate over it - if ($iterator_atomic_type instanceof Type\Atomic\TArray + if ($iterator_atomic_type instanceof TArray && $iterator_atomic_type->type_params[1]->isEmpty() ) { $always_non_empty_array = false; @@ -418,42 +443,42 @@ public static function checkIteratorType( continue; } - if ($iterator_atomic_type instanceof Type\Atomic\TNull - || $iterator_atomic_type instanceof Type\Atomic\TFalse + if ($iterator_atomic_type instanceof TNull + || $iterator_atomic_type instanceof TFalse ) { $always_non_empty_array = false; continue; } - if ($iterator_atomic_type instanceof Type\Atomic\TArray - || $iterator_atomic_type instanceof Type\Atomic\TKeyedArray - || $iterator_atomic_type instanceof Type\Atomic\TList + if ($iterator_atomic_type instanceof TArray + || $iterator_atomic_type instanceof TKeyedArray + || $iterator_atomic_type instanceof TList ) { - if ($iterator_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($iterator_atomic_type instanceof TKeyedArray) { if (!$iterator_atomic_type->sealed) { $always_non_empty_array = false; } $iterator_atomic_type = $iterator_atomic_type->getGenericArrayType(); - } elseif ($iterator_atomic_type instanceof Type\Atomic\TList) { + } elseif ($iterator_atomic_type instanceof TList) { $list_var_id = ExpressionIdentifier::getArrayVarId( $expr, $statements_analyzer->getFQCLN(), $statements_analyzer ); - if (!$iterator_atomic_type instanceof Type\Atomic\TNonEmptyList) { + if (!$iterator_atomic_type instanceof TNonEmptyList) { $always_non_empty_array = false; } - $iterator_atomic_type = new Type\Atomic\TArray([ + $iterator_atomic_type = new TArray([ $list_var_id - ? new Type\Union([ - new Type\Atomic\TDependentListKey($list_var_id) + ? new Union([ + new TDependentListKey($list_var_id) ]) - : new Type\Union([new Type\Atomic\TIntRange(0, null)]), + : new Union([new TIntRange(0, null)]), $iterator_atomic_type->type_param ]); - } elseif (!$iterator_atomic_type instanceof Type\Atomic\TNonEmptyArray) { + } elseif (!$iterator_atomic_type instanceof TNonEmptyArray) { $always_non_empty_array = false; } @@ -477,15 +502,15 @@ public static function checkIteratorType( $always_non_empty_array = false; - if ($iterator_atomic_type instanceof Type\Atomic\Scalar || - $iterator_atomic_type instanceof Type\Atomic\TVoid + if ($iterator_atomic_type instanceof Scalar || + $iterator_atomic_type instanceof TVoid ) { $invalid_iterator_types[] = $iterator_atomic_type->getKey(); $value_type = Type::getMixed(); - } elseif ($iterator_atomic_type instanceof Type\Atomic\TObject || - $iterator_atomic_type instanceof Type\Atomic\TMixed || - $iterator_atomic_type instanceof Type\Atomic\TEmpty + } elseif ($iterator_atomic_type instanceof TObject || + $iterator_atomic_type instanceof TMixed || + $iterator_atomic_type instanceof TEmpty ) { $has_valid_iterator = true; $value_type = Type::getMixed(); @@ -516,7 +541,7 @@ public static function checkIteratorType( $statements_analyzer->getSuppressedIssues() ); } - } elseif ($iterator_atomic_type instanceof Type\Atomic\TIterable) { + } elseif ($iterator_atomic_type instanceof TIterable) { if ($iterator_atomic_type->extra_types) { $iterator_atomic_type_copy = clone $iterator_atomic_type; $iterator_atomic_type_copy->extra_types = []; @@ -533,7 +558,7 @@ public static function checkIteratorType( $intersection_key_type = null; foreach ($iterator_atomic_types as $iat) { - if (!$iat instanceof Type\Atomic\TIterable) { + if (!$iat instanceof TIterable) { continue; } @@ -594,7 +619,7 @@ public static function checkIteratorType( $statements_analyzer->getSuppressedIssues() ); } - } elseif ($iterator_atomic_type instanceof Type\Atomic\TNamedObject) { + } elseif ($iterator_atomic_type instanceof TNamedObject) { if ($iterator_atomic_type->value !== 'Traversable' && $iterator_atomic_type->value !== $statements_analyzer->getClassName() ) { @@ -614,7 +639,7 @@ public static function checkIteratorType( if (AtomicTypeComparator::isContainedBy( $codebase, $iterator_atomic_type, - new Type\Atomic\TIterable([Type::getMixed(), Type::getMixed()]) + new TIterable([Type::getMixed(), Type::getMixed()]) )) { self::handleIterable( $statements_analyzer, @@ -695,12 +720,12 @@ public static function checkIteratorType( public static function handleIterable( StatementsAnalyzer $statements_analyzer, - Type\Atomic\TNamedObject $iterator_atomic_type, + TNamedObject $iterator_atomic_type, PhpParser\Node\Expr $foreach_expr, Codebase $codebase, Context $context, - ?Type\Union &$key_type, - ?Type\Union &$value_type, + ?Union &$key_type, + ?Union &$value_type, bool &$has_valid_iterator ): void { if ($iterator_atomic_type->extra_types) { @@ -713,8 +738,8 @@ public static function handleIterable( } foreach ($iterator_atomic_types as $iterator_atomic_type) { - if ($iterator_atomic_type instanceof Type\Atomic\TTemplateParam - || $iterator_atomic_type instanceof Type\Atomic\TObjectWithProperties + if ($iterator_atomic_type instanceof TTemplateParam + || $iterator_atomic_type instanceof TObjectWithProperties ) { throw new UnexpectedValueException('Shouldn’t get a generic param here'); } @@ -722,12 +747,12 @@ public static function handleIterable( $has_valid_iterator = true; - if ($iterator_atomic_type instanceof Type\Atomic\TNamedObject + if ($iterator_atomic_type instanceof TNamedObject && strtolower($iterator_atomic_type->value) === 'simplexmlelement' ) { $value_type = Type::combineUnionTypes( $value_type, - new Type\Union([clone $iterator_atomic_type]) + new Union([clone $iterator_atomic_type]) ); $key_type = Type::combineUnionTypes( @@ -736,7 +761,7 @@ public static function handleIterable( ); } - if ($iterator_atomic_type instanceof Type\Atomic\TIterable + if ($iterator_atomic_type instanceof TIterable || (strtolower($iterator_atomic_type->value) === 'traversable' || $codebase->classImplements( $iterator_atomic_type->value, @@ -810,16 +835,16 @@ public static function handleIterable( $key_type_part = null; $value_type_part = null; - if ($array_atomic_type instanceof Type\Atomic\TArray - || $array_atomic_type instanceof Type\Atomic\TKeyedArray + if ($array_atomic_type instanceof TArray + || $array_atomic_type instanceof TKeyedArray ) { - if ($array_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($array_atomic_type instanceof TKeyedArray) { $array_atomic_type = $array_atomic_type->getGenericArrayType(); } [$key_type_part, $value_type_part] = $array_atomic_type->type_params; } else { - if ($array_atomic_type instanceof Type\Atomic\TNamedObject + if ($array_atomic_type instanceof TNamedObject && $codebase->classExists($array_atomic_type->value) && $codebase->classImplements( $array_atomic_type->value, @@ -854,8 +879,8 @@ public static function handleIterable( } } - if ($array_atomic_type instanceof Type\Atomic\TIterable - || ($array_atomic_type instanceof Type\Atomic\TNamedObject + if ($array_atomic_type instanceof TIterable + || ($array_atomic_type instanceof TNamedObject && ($array_atomic_type->value === 'Traversable' || ($codebase->classOrInterfaceExists($array_atomic_type->value) && $codebase->classImplements( @@ -934,13 +959,13 @@ public static function handleIterable( } public static function getKeyValueParamsForTraversableObject( - Type\Atomic $iterator_atomic_type, + Atomic $iterator_atomic_type, Codebase $codebase, - ?Type\Union &$key_type, - ?Type\Union &$value_type + ?Union &$key_type, + ?Union &$value_type ): void { - if ($iterator_atomic_type instanceof Type\Atomic\TIterable - || ($iterator_atomic_type instanceof Type\Atomic\TGenericObject + if ($iterator_atomic_type instanceof TIterable + || ($iterator_atomic_type instanceof TGenericObject && strtolower($iterator_atomic_type->value) === 'traversable') ) { $value_type = Type::combineUnionTypes($value_type, $iterator_atomic_type->type_params[1]); @@ -949,7 +974,7 @@ public static function getKeyValueParamsForTraversableObject( return; } - if ($iterator_atomic_type instanceof Type\Atomic\TNamedObject + if ($iterator_atomic_type instanceof TNamedObject && ( $codebase->classImplements( $iterator_atomic_type->value, @@ -970,16 +995,16 @@ public static function getKeyValueParamsForTraversableObject( } if ($generic_storage->template_types - || $iterator_atomic_type instanceof Type\Atomic\TGenericObject + || $iterator_atomic_type instanceof TGenericObject ) { // if we're just being passed the non-generic class itself, assume // that it's inside the calling class - $passed_type_params = $iterator_atomic_type instanceof Type\Atomic\TGenericObject + $passed_type_params = $iterator_atomic_type instanceof TGenericObject ? $iterator_atomic_type->type_params : array_values( array_map( - /** @param array $arr */ - function (array $arr) use ($iterator_atomic_type): Type\Union { + /** @param array $arr */ + function (array $arr) use ($iterator_atomic_type): Union { return $arr[$iterator_atomic_type->value] ?? Type::getMixed(); }, $generic_storage->template_types @@ -1016,7 +1041,7 @@ private static function getFakeMethodCallType( PhpParser\Node\Expr $foreach_expr, Context $context, string $method_name - ): ?Type\Union { + ): ?Union { $old_data_provider = $statements_analyzer->node_data; $statements_analyzer->node_data = clone $statements_analyzer->node_data; @@ -1064,9 +1089,9 @@ private static function getFakeMethodCallType( } /** - * @param array> $template_extended_params - * @param array> $class_template_types - * @param array $calling_type_params + * @param array> $template_extended_params + * @param array> $class_template_types + * @param array $calling_type_params */ private static function getExtendedType( string $template_name, @@ -1075,7 +1100,7 @@ private static function getExtendedType( array $template_extended_params, ?array $class_template_types = null, ?array $calling_type_params = null - ): ?Type\Union { + ): ?Union { if ($calling_class === $template_class) { if (isset($class_template_types[$template_name]) && $calling_type_params) { $offset = array_search($template_name, array_keys($class_template_types)); @@ -1094,7 +1119,7 @@ private static function getExtendedType( $return_type = null; foreach ($extended_type->getAtomicTypes() as $extended_atomic_type) { - if (!$extended_atomic_type instanceof Type\Atomic\TTemplateParam) { + if (!$extended_atomic_type instanceof TTemplateParam) { $return_type = Type::combineUnionTypes( $return_type, $extended_type diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php index 3494d56883e..cd4002e511d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php @@ -1,4 +1,5 @@ inside_conditional = false; - } + $outer_context->inside_conditional = $was_inside_conditional; if (!$if_context) { $if_context = clone $outer_context; @@ -162,13 +161,15 @@ function (Clause $c) use ($changed_var_ids): bool { $referenced_var_ids = $first_cond_referenced_var_ids; $if_conditional_context->referenced_var_ids = []; + $was_inside_conditional = $if_conditional_context->inside_conditional; + $if_conditional_context->inside_conditional = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $cond, $if_conditional_context) === false) { throw new ScopeAnalysisException(); } - $if_conditional_context->inside_conditional = false; + $if_conditional_context->inside_conditional = $was_inside_conditional; /** @var array */ $more_cond_referenced_var_ids = $if_conditional_context->referenced_var_ids; @@ -201,11 +202,11 @@ function (Clause $c) use ($changed_var_ids): bool { $newish_var_ids = array_map( /** - * @param Type\Union $_ + * @param Union $_ * * @return true */ - function (Type\Union $_): bool { + function (Union $_): bool { return true; }, array_diff_key( diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php index 81831cf3514..f2071b329ea 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php @@ -1,4 +1,5 @@ $pre_assignment_else_redefined_vars + * @param array $pre_assignment_else_redefined_vars * * @return false|null */ diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php index 8381ad24e3c..1f9dcc35d81 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php @@ -1,4 +1,5 @@ elseifs as $elseif) { - if (IfElse\ElseIfAnalyzer::analyze( + if (ElseIfAnalyzer::analyze( $statements_analyzer, $elseif, $if_scope, @@ -396,7 +400,7 @@ function (array $carry, Clause $clause): array { } } - if (IfElse\ElseAnalyzer::analyze( + if (ElseAnalyzer::analyze( $statements_analyzer, $stmt->else, $if_scope, diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php index 9b8d7db4add..1cfd4023eee 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php @@ -1,4 +1,5 @@ referenced_var_ids; $loop_context->referenced_var_ids = []; + $was_inside_conditional = $loop_context->inside_conditional; + $loop_context->inside_conditional = true; $suppressed_issues = $statements_analyzer->getSuppressedIssues(); @@ -636,10 +639,12 @@ private static function applyPreConditionToLoopContext( } if (ExpressionAnalyzer::analyze($statements_analyzer, $pre_condition, $loop_context) === false) { + $loop_context->inside_conditional = $was_inside_conditional; + return []; } - $loop_context->inside_conditional = false; + $loop_context->inside_conditional = $was_inside_conditional; $new_referenced_var_ids = $loop_context->referenced_var_ids; $loop_context->referenced_var_ids = array_merge($pre_referenced_var_ids, $new_referenced_var_ids); diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php index b827e97804a..fd492eba78d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php @@ -1,4 +1,5 @@ getCodebase(); + $was_inside_conditional = $context->inside_conditional; + $context->inside_conditional = true; + if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->cond, $context) === false) { + $context->inside_conditional = $was_inside_conditional; + return; } - $context->inside_conditional = false; + $context->inside_conditional = $was_inside_conditional; $switch_var_id = ExpressionIdentifier::getArrayVarId( $stmt->cond, diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php index 4cd3d80e0b3..1afb4075013 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php @@ -1,4 +1,5 @@ inside_conditional = false; - } + $case_context->inside_conditional = $was_inside_conditional; $statements_analyzer->node_data = clone $statements_analyzer->node_data; @@ -142,7 +144,7 @@ public static function analyze( $type_statements = []; foreach ($switch_var_type->getAtomicTypes() as $type) { - if ($type instanceof Type\Atomic\TDependentGetClass) { + if ($type instanceof TDependentGetClass) { $type_statements[] = new VirtualFuncCall( new VirtualName(['get_class']), [ @@ -158,7 +160,7 @@ public static function analyze( ], $stmt->cond->getAttributes() ); - } elseif ($type instanceof Type\Atomic\TDependentGetType) { + } elseif ($type instanceof TDependentGetType) { $type_statements[] = new VirtualFuncCall( new VirtualName(['gettype']), [ @@ -174,7 +176,7 @@ public static function analyze( ], $stmt->cond->getAttributes() ); - } elseif ($type instanceof Type\Atomic\TDependentGetDebugType) { + } elseif ($type instanceof TDependentGetDebugType) { $type_statements[] = new VirtualFuncCall( new VirtualName(['get_debug_type']), [ diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php index 0b67aa81ed2..8ecffb85d89 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php @@ -1,4 +1,5 @@ taints = [ - Type\TaintKind::INPUT_HTML, - Type\TaintKind::INPUT_HAS_QUOTES, - Type\TaintKind::USER_SECRET, - Type\TaintKind::SYSTEM_SECRET + TaintKind::INPUT_HTML, + TaintKind::INPUT_HAS_QUOTES, + TaintKind::USER_SECRET, + TaintKind::SYSTEM_SECRET ]; $statements_analyzer->data_flow_graph->addSink($echo_param_sink); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index aafdd9648e0..7db0f813239 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -1,4 +1,5 @@ can_create_objectlike && $array_creation_info->property_types ) { - $object_like = new Type\Atomic\TKeyedArray( + $object_like = new TKeyedArray( $array_creation_info->property_types, $array_creation_info->class_strings ); $object_like->sealed = true; $object_like->is_list = $array_creation_info->all_list; - $stmt_type = new Type\Union([$object_like]); + $stmt_type = new Union([$object_like]); if ($array_creation_info->parent_taint_nodes) { $stmt_type->parent_nodes = $array_creation_info->parent_taint_nodes; @@ -121,13 +144,13 @@ public static function analyze( if ($array_creation_info->all_list) { if (empty($array_creation_info->item_key_atomic_types)) { - $array_type = new Type\Atomic\TList($item_value_type ?? Type::getMixed()); + $array_type = new TList($item_value_type ?? Type::getMixed()); } else { - $array_type = new Type\Atomic\TNonEmptyList($item_value_type ?? Type::getMixed()); + $array_type = new TNonEmptyList($item_value_type ?? Type::getMixed()); $array_type->count = count($array_creation_info->property_types); } - $stmt_type = new Type\Union([ + $stmt_type = new Union([ $array_type, ]); @@ -145,7 +168,7 @@ public static function analyze( $good_types = []; foreach ($item_key_type->getAtomicTypes() as $atomic_key_type) { - if ($atomic_key_type instanceof Type\Atomic\TMixed) { + if ($atomic_key_type instanceof TMixed) { IssueBuffer::maybeAdd( new MixedArrayOffset( 'Cannot create mixed offset – expecting array-key', @@ -156,19 +179,19 @@ public static function analyze( $bad_types[] = $atomic_key_type; - $good_types[] = new Type\Atomic\TArrayKey; + $good_types[] = new TArrayKey; continue; } - if (!$atomic_key_type instanceof Type\Atomic\TString - && !$atomic_key_type instanceof Type\Atomic\TInt - && !$atomic_key_type instanceof Type\Atomic\TArrayKey - && !$atomic_key_type instanceof Type\Atomic\TMixed - && !$atomic_key_type instanceof Type\Atomic\TTemplateParam + if (!$atomic_key_type instanceof TString + && !$atomic_key_type instanceof TInt + && !$atomic_key_type instanceof TArrayKey + && !$atomic_key_type instanceof TMixed + && !$atomic_key_type instanceof TTemplateParam && !( - $atomic_key_type instanceof Type\Atomic\TObjectWithProperties + $atomic_key_type instanceof TObjectWithProperties && isset($atomic_key_type->methods['__toString']) ) ) { @@ -182,19 +205,19 @@ public static function analyze( $bad_types[] = $atomic_key_type; - if ($atomic_key_type instanceof Type\Atomic\TFalse) { - $good_types[] = new Type\Atomic\TLiteralInt(0); - } elseif ($atomic_key_type instanceof Type\Atomic\TTrue) { - $good_types[] = new Type\Atomic\TLiteralInt(1); - } elseif ($atomic_key_type instanceof Type\Atomic\TBool) { - $good_types[] = new Type\Atomic\TLiteralInt(0); - $good_types[] = new Type\Atomic\TLiteralInt(1); - } elseif ($atomic_key_type instanceof Type\Atomic\TLiteralFloat) { - $good_types[] = new Type\Atomic\TLiteralInt((int) $atomic_key_type->value); - } elseif ($atomic_key_type instanceof Type\Atomic\TFloat) { - $good_types[] = new Type\Atomic\TInt; + if ($atomic_key_type instanceof TFalse) { + $good_types[] = new TLiteralInt(0); + } elseif ($atomic_key_type instanceof TTrue) { + $good_types[] = new TLiteralInt(1); + } elseif ($atomic_key_type instanceof TBool) { + $good_types[] = new TLiteralInt(0); + $good_types[] = new TLiteralInt(1); + } elseif ($atomic_key_type instanceof TLiteralFloat) { + $good_types[] = new TLiteralInt((int) $atomic_key_type->value); + } elseif ($atomic_key_type instanceof TFloat) { + $good_types[] = new TInt; } else { - $good_types[] = new Type\Atomic\TArrayKey; + $good_types[] = new TArrayKey; } } } @@ -207,14 +230,14 @@ public static function analyze( } } - $array_type = new Type\Atomic\TNonEmptyArray([ + $array_type = new TNonEmptyArray([ $item_key_type && !$item_key_type->hasMixed() ? $item_key_type : Type::getArrayKey(), $item_value_type ?? Type::getMixed(), ]); $array_type->count = count($array_creation_info->property_types); - $stmt_type = new Type\Union([ + $stmt_type = new Union([ $array_type, ]); @@ -288,6 +311,8 @@ private static function analyzeArrayItem( $was_inside_general_use = $context->inside_general_use; $context->inside_general_use = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $item->key, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return; } $context->inside_general_use = $was_inside_general_use; @@ -318,7 +343,7 @@ private static function analyzeArrayItem( $item_key_literal_type = $key_type->getSingleStringLiteral(); $item_key_value = $item_key_literal_type->value; - if ($item_key_literal_type instanceof Type\Atomic\TLiteralClassString) { + if ($item_key_literal_type instanceof TLiteralClassString) { $array_creation_info->class_strings[$item_key_value] = true; } } elseif ($key_type->isSingleIntLiteral()) { @@ -335,7 +360,7 @@ private static function analyzeArrayItem( } else { $item_is_list_item = true; $item_key_value = $array_creation_info->int_offset++; - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralInt($item_key_value); + $array_creation_info->item_key_atomic_types[] = new TLiteralInt($item_key_value); } if (ExpressionAnalyzer::analyze($statements_analyzer, $item->value, $context) === false) { @@ -465,7 +490,7 @@ private static function analyzeArrayItem( array_values($item_value_type->getAtomicTypes()) ); } else { - $array_creation_info->item_value_atomic_types[] = new Type\Atomic\TMixed(); + $array_creation_info->item_value_atomic_types[] = new TMixed(); if ($item_key_value !== null && count($array_creation_info->property_types) <= 100) { $array_creation_info->property_types[$item_key_value] = Type::getMixed(); @@ -479,11 +504,11 @@ private static function handleUnpackedArray( StatementsAnalyzer $statements_analyzer, ArrayCreationInfo $array_creation_info, PhpParser\Node\Expr\ArrayItem $item, - Type\Union $unpacked_array_type, + Union $unpacked_array_type, Codebase $codebase ): void { foreach ($unpacked_array_type->getAtomicTypes() as $unpacked_atomic_type) { - if ($unpacked_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($unpacked_atomic_type instanceof TKeyedArray) { foreach ($unpacked_atomic_type->properties as $key => $property_value) { if (is_string($key)) { if ($codebase->php_major_version < 8 || @@ -500,10 +525,10 @@ private static function handleUnpackedArray( return; } $new_offset = $key; - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralString($new_offset); + $array_creation_info->item_key_atomic_types[] = new TLiteralString($new_offset); } else { $new_offset = $array_creation_info->int_offset++; - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralInt($new_offset); + $array_creation_info->item_key_atomic_types[] = new TLiteralInt($new_offset); } $array_creation_info->item_value_atomic_types = array_merge( @@ -517,10 +542,10 @@ private static function handleUnpackedArray( } else { $codebase = $statements_analyzer->getCodebase(); - if ($unpacked_atomic_type instanceof Type\Atomic\TArray - || $unpacked_atomic_type instanceof Type\Atomic\TIterable + if ($unpacked_atomic_type instanceof TArray + || $unpacked_atomic_type instanceof TIterable || ( - $unpacked_atomic_type instanceof Type\Atomic\TGenericObject + $unpacked_atomic_type instanceof TGenericObject && $unpacked_atomic_type->hasTraversableInterface($codebase) && count($unpacked_atomic_type->type_params) === 2 )) { @@ -545,9 +570,9 @@ private static function handleUnpackedArray( return; } - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TString(); + $array_creation_info->item_key_atomic_types[] = new TString(); } elseif ($unpacked_atomic_type->type_params[0]->hasInt()) { - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TInt(); + $array_creation_info->item_key_atomic_types[] = new TInt(); } $array_creation_info->item_value_atomic_types = array_merge( @@ -555,16 +580,16 @@ private static function handleUnpackedArray( array_values( isset($unpacked_atomic_type->type_params[1]) ? $unpacked_atomic_type->type_params[1]->getAtomicTypes() - : [new Type\Atomic\TMixed()] + : [new TMixed()] ) ); - } elseif ($unpacked_atomic_type instanceof Type\Atomic\TList) { + } elseif ($unpacked_atomic_type instanceof TList) { if ($unpacked_atomic_type->type_param->isEmpty()) { continue; } $array_creation_info->can_create_objectlike = false; - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TInt(); + $array_creation_info->item_key_atomic_types[] = new TInt(); $array_creation_info->item_value_atomic_types = array_merge( $array_creation_info->item_value_atomic_types, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php index 2796b2e2715..ca0e761b283 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php @@ -1,23 +1,25 @@ + * @var list */ public $item_key_atomic_types = []; /** - * @var list + * @var list */ public $item_value_atomic_types = []; /** - * @var array + * @var array */ public $property_types = []; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index bd373377c2b..6568ecd11c8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -1,4 +1,5 @@ getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TLiteralClassString) { + if ($atomic_type instanceof TLiteralClassString) { $literal_class_strings[] = $atomic_type->value; - } elseif ($atomic_type instanceof Type\Atomic\TTemplateParamClass) { + } elseif ($atomic_type instanceof TTemplateParamClass) { $literal_class_strings[] = $atomic_type->param_name; - } elseif ($atomic_type instanceof Type\Atomic\TClassString && $atomic_type->as !== 'object') { + } elseif ($atomic_type instanceof TClassString && $atomic_type->as !== 'object') { $literal_class_strings[] = $atomic_type->as; } } @@ -1419,7 +1435,7 @@ protected static function hasGetClassCheck( if ($left_type && $left_type->isSingle()) { foreach ($left_type->getAtomicTypes() as $type_part) { - if ($type_part instanceof Type\Atomic\TClassString) { + if ($type_part instanceof TClassString) { $left_class_string_t = true; break; } @@ -1458,7 +1474,7 @@ protected static function hasGetClassCheck( if ($right_type && $right_type->isSingle()) { foreach ($right_type->getAtomicTypes() as $type_part) { - if ($type_part instanceof Type\Atomic\TClassString) { + if ($type_part instanceof TClassString) { $right_class_string_t = true; break; } @@ -1842,7 +1858,7 @@ private static function handleIsTypeCheck( FileSource $source, PhpParser\Node\Expr\FuncCall $stmt, ?string $first_var_name, - ?Type\Union $first_var_type, + ?Union $first_var_type, PhpParser\Node\Expr\FuncCall $expr, bool $negate ): array { @@ -1869,7 +1885,7 @@ private static function handleIsTypeCheck( $callable = self::IS_TYPE_CHECKS[$function_name][1]; assert(is_callable($callable)); $type = $callable(); - assert($type instanceof Type\Union); + assert($type instanceof Union); self::processIrreconcilableFunctionCall( $first_var_type, $type, @@ -2562,7 +2578,7 @@ private static function getGetclassInequalityAssertions( if ($type && $var_name) { foreach ($type->getAtomicTypes() as $type_part) { - if ($type_part instanceof Type\Atomic\TTemplateParamClass) { + if ($type_part instanceof TTemplateParamClass) { $if_types[$var_name] = [['!=' . $type_part->param_name]]; } } @@ -3262,7 +3278,7 @@ private static function getGetclassEqualityAssertions( if ($type && $var_name) { foreach ($type->getAtomicTypes() as $type_part) { - if ($type_part instanceof Type\Atomic\TTemplateParamClass) { + if ($type_part instanceof TTemplateParamClass) { $if_types[$var_name] = [['=' . $type_part->param_name]]; } } @@ -3486,7 +3502,7 @@ private static function getIsaAssertions( $vals = []; foreach ($second_arg_type->getAtomicTypes() as $second_arg_atomic_type) { - if ($second_arg_atomic_type instanceof Type\Atomic\TTemplateParamClass) { + if ($second_arg_atomic_type instanceof TTemplateParamClass) { $vals[] = [$is_a_prefix . $second_arg_atomic_type->param_name]; } } @@ -3520,14 +3536,14 @@ private static function getInarrayAssertions( && !$expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch ) { foreach ($second_arg_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TArray - || $atomic_type instanceof Type\Atomic\TKeyedArray - || $atomic_type instanceof Type\Atomic\TList + if ($atomic_type instanceof TArray + || $atomic_type instanceof TKeyedArray + || $atomic_type instanceof TList ) { $is_sealed = false; - if ($atomic_type instanceof Type\Atomic\TList) { + if ($atomic_type instanceof TList) { $value_type = $atomic_type->type_param; - } elseif ($atomic_type instanceof Type\Atomic\TKeyedArray) { + } elseif ($atomic_type instanceof TKeyedArray) { $value_type = $atomic_type->getGenericValueType(); $is_sealed = $atomic_type->sealed; } else { @@ -3551,18 +3567,18 @@ private static function getInarrayAssertions( } } else { foreach ($value_type->getAtomicTypes() as $atomic_value_type) { - if ($atomic_value_type instanceof Type\Atomic\TLiteralInt - || $atomic_value_type instanceof Type\Atomic\TLiteralString - || $atomic_value_type instanceof Type\Atomic\TLiteralFloat - || $atomic_value_type instanceof Type\Atomic\TEnumCase + if ($atomic_value_type instanceof TLiteralInt + || $atomic_value_type instanceof TLiteralString + || $atomic_value_type instanceof TLiteralFloat + || $atomic_value_type instanceof TEnumCase ) { $assertions[] = '=' . $atomic_value_type->getAssertionString(); - } elseif ($atomic_value_type instanceof Type\Atomic\TFalse - || $atomic_value_type instanceof Type\Atomic\TTrue - || $atomic_value_type instanceof Type\Atomic\TNull + } elseif ($atomic_value_type instanceof TFalse + || $atomic_value_type instanceof TTrue + || $atomic_value_type instanceof TNull ) { $assertions[] = $atomic_value_type->getAssertionString(); - } elseif (!$atomic_value_type instanceof Type\Atomic\TMixed) { + } elseif (!$atomic_value_type instanceof TMixed) { // mixed doesn't tell us anything and can be omitted. // // For the meaning of in-array, see the above comment. @@ -3583,13 +3599,13 @@ private static function getInarrayAssertions( /** * @param PhpParser\Node\Expr\FuncCall $expr - * @param Type\Union|null $first_var_type + * @param Union|null $first_var_type * @param string|null $first_var_name * @return list>>> */ private static function getArrayKeyExistsAssertions( PhpParser\Node\Expr\FuncCall $expr, - ?Type\Union $first_var_type, + ?Union $first_var_type, ?string $first_var_name, FileSource $source, ?string $this_class_name @@ -3607,10 +3623,10 @@ private static function getArrayKeyExistsAssertions( && ($second_var_type = $source->node_data->getType($expr->getArgs()[1]->value)) ) { foreach ($second_var_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TArray - || $atomic_type instanceof Type\Atomic\TKeyedArray + if ($atomic_type instanceof TArray + || $atomic_type instanceof TKeyedArray ) { - if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_type instanceof TKeyedArray) { $key_possibly_undefined = false; foreach ($atomic_type->properties as $property_type) { @@ -4027,9 +4043,9 @@ private static function getInstanceofAssertions( */ private static function handleParadoxicalAssertions( StatementsAnalyzer $source, - Type\Union $var_type, + Union $var_type, ?string $this_class_name, - Type\Union $other_type, + Union $other_type, Codebase $codebase, PhpParser\Node\Expr\BinaryOp $conditional ): void { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php index e05106628ed..a2eb2c60f0a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php @@ -1,4 +1,5 @@ value); + $key_values[] = new TLiteralString($current_dim->value); } elseif ($current_dim instanceof PhpParser\Node\Scalar\LNumber && !$root_is_string) { - $key_values[] = new Type\Atomic\TLiteralInt($current_dim->value); + $key_values[] = new TLiteralInt($current_dim->value); } elseif ($current_dim && ($key_type = $statements_analyzer->node_data->getType($current_dim)) && !$root_is_string @@ -275,21 +287,21 @@ public static function updateArrayType( } /** - * @param non-empty-list $key_values + * @param non-empty-list $key_values */ private static function updateTypeWithKeyValues( Codebase $codebase, - Type\Union $child_stmt_type, - Type\Union $current_type, + Union $child_stmt_type, + Union $current_type, array $key_values - ): Type\Union { + ): Union { $has_matching_objectlike_property = false; $has_matching_string = false; $child_stmt_type = clone $child_stmt_type; foreach ($child_stmt_type->getAtomicTypes() as $type) { - if ($type instanceof Type\Atomic\TTemplateParam) { + if ($type instanceof TTemplateParam) { $type->as = self::updateTypeWithKeyValues( $codebase, $type->as, @@ -299,7 +311,7 @@ private static function updateTypeWithKeyValues( $has_matching_objectlike_property = true; - $child_stmt_type->substitute(new Type\Union([$type]), $type->as); + $child_stmt_type->substitute(new Union([$type]), $type->as); continue; } @@ -311,12 +323,12 @@ private static function updateTypeWithKeyValues( $type->properties[$key_value->value] = clone $current_type; } - } elseif ($type instanceof Type\Atomic\TString - && $key_value instanceof Type\Atomic\TLiteralInt + } elseif ($type instanceof TString + && $key_value instanceof TLiteralInt ) { $has_matching_string = true; - if ($type instanceof Type\Atomic\TLiteralString + if ($type instanceof TLiteralString && $current_type->isSingleStringLiteral() ) { $new_char = $current_type->getSingleStringLiteral()->value; @@ -326,7 +338,7 @@ private static function updateTypeWithKeyValues( } } } elseif ($type instanceof TNonEmptyList - && $key_value instanceof Type\Atomic\TLiteralInt + && $key_value instanceof TLiteralInt && count($key_values) === 1 ) { $has_matching_objectlike_property = true; @@ -350,22 +362,22 @@ private static function updateTypeWithKeyValues( $object_like = new TKeyedArray( [$key_value->value => clone $current_type], - $key_value instanceof Type\Atomic\TLiteralClassString + $key_value instanceof TLiteralClassString ? [$key_value->value => true] : null ); $object_like->sealed = true; - $array_assignment_type = new Type\Union([ + $array_assignment_type = new Union([ $object_like, ]); } else { $array_assignment_literals = $key_values; - $array_assignment_type = new Type\Union([ - new Type\Atomic\TNonEmptyArray([ - new Type\Union($array_assignment_literals), + $array_assignment_type = new Union([ + new TNonEmptyArray([ + new Union($array_assignment_literals), clone $current_type ]) ]); @@ -384,13 +396,13 @@ private static function updateTypeWithKeyValues( } /** - * @param list $key_values $key_values + * @param list $key_values $key_values */ private static function taintArrayAssignment( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\ArrayDimFetch $expr, - Type\Union $stmt_type, - Type\Union $child_stmt_type, + Union $stmt_type, + Union $child_stmt_type, ?string $var_var_id, array $key_values ): void { @@ -454,12 +466,12 @@ private static function updateArrayAssignmentChildType( Codebase $codebase, ?PhpParser\Node\Expr $current_dim, Context $context, - Type\Union $value_type, - Type\Union $root_type, + Union $value_type, + Union $root_type, bool $offset_already_existed, ?PhpParser\Node\Expr $child_stmt, ?string $parent_var_id - ): Type\Union { + ): Union { $templated_assignment = false; if ($current_dim) { @@ -473,13 +485,13 @@ private static function updateArrayAssignmentChildType( if ($key_type->isSingle()) { $key_type_type = $key_type->getSingleAtomic(); - if ($key_type_type instanceof Type\Atomic\TDependentListKey + if ($key_type_type instanceof TDependentListKey && $key_type_type->getVarId() === $parent_var_id ) { $offset_already_existed = true; } - if ($key_type_type instanceof Type\Atomic\TTemplateParam + if ($key_type_type instanceof TTemplateParam && $key_type_type->as->isSingle() && $root_type->isSingle() && $value_type->isSingle() @@ -488,9 +500,9 @@ private static function updateArrayAssignmentChildType( $value_atomic_type = $value_type->getSingleAtomic(); $root_atomic_type = $root_type->getSingleAtomic(); - if ($key_type_as_type instanceof Type\Atomic\TTemplateKeyOf - && $root_atomic_type instanceof Type\Atomic\TTemplateParam - && $value_atomic_type instanceof Type\Atomic\TTemplateIndexedAccess + if ($key_type_as_type instanceof TTemplateKeyOf + && $root_atomic_type instanceof TTemplateParam + && $value_atomic_type instanceof TTemplateIndexedAccess && $key_type_as_type->param_name === $root_atomic_type->param_name && $key_type_as_type->defining_class === $root_atomic_type->defining_class && $value_atomic_type->array_param_name === $root_atomic_type->param_name @@ -524,12 +536,12 @@ private static function updateArrayAssignmentChildType( && $key_type->isTemplatedClassString() ) { /** - * @var Type\Atomic\TClassStringMap + * @var TClassStringMap * @psalm-suppress PossiblyUndefinedStringArrayOffset */ $class_string_map = $parent_type->getAtomicTypes()['array']; /** - * @var Type\Atomic\TTemplateParamClass + * @var TTemplateParamClass */ $offset_type_part = $key_type->getSingleAtomic(); @@ -537,11 +549,11 @@ private static function updateArrayAssignmentChildType( [], [ $offset_type_part->param_name => [ - $offset_type_part->defining_class => new Type\Union([ - new Type\Atomic\TTemplateParam( + $offset_type_part->defining_class => new Union([ + new TTemplateParam( $class_string_map->param_name, $offset_type_part->as_type - ? new Type\Union([$offset_type_part->as_type]) + ? new Union([$offset_type_part->as_type]) : Type::getObject(), 'class-string-map' ) @@ -556,7 +568,7 @@ private static function updateArrayAssignmentChildType( $codebase ); - $array_atomic_type = new Type\Atomic\TClassStringMap( + $array_atomic_type = new TClassStringMap( $class_string_map->param_name, $class_string_map->as_type, $value_type @@ -585,7 +597,7 @@ private static function updateArrayAssignmentChildType( $atomic_root_types = $root_type->getAtomicTypes(); if (isset($atomic_root_types['array'])) { - if ($array_atomic_type instanceof Type\Atomic\TClassStringMap) { + if ($array_atomic_type instanceof TClassStringMap) { $array_atomic_type = new TNonEmptyArray([ $array_atomic_type->getStandinKeyParam(), $array_atomic_type->value_param @@ -605,7 +617,7 @@ private static function updateArrayAssignmentChildType( ) { $array_atomic_type = clone $atomic_root_types['array']; - $new_child_type = new Type\Union([$array_atomic_type]); + $new_child_type = new Union([$array_atomic_type]); $new_child_type->parent_nodes = $root_type->parent_nodes; } @@ -621,7 +633,7 @@ private static function updateArrayAssignmentChildType( } } - $array_assignment_type = new Type\Union([ + $array_assignment_type = new Union([ $array_atomic_type, ]); @@ -662,13 +674,13 @@ private static function analyzeNestedArrayAssignment( Codebase $codebase, Context $context, ?PhpParser\Node\Expr $assign_value, - Type\Union $assignment_type, + Union $assignment_type, array $child_stmts, ?string $root_var_id, ?string &$parent_var_id, ?PhpParser\Node\Expr &$child_stmt, - Type\Union &$root_type, - Type\Union &$current_type, + Union &$root_type, + Union &$current_type, ?PhpParser\Node\Expr &$current_dim, bool &$offset_already_existed ): void { @@ -700,6 +712,8 @@ private static function analyzeNestedArrayAssignment( $child_stmt->dim, $context ) === false) { + $context->inside_general_use = $was_inside_general_use; + return; } @@ -832,9 +846,9 @@ private static function analyzeNestedArrayAssignment( $key_values = []; if ($current_dim instanceof PhpParser\Node\Scalar\String_) { - $key_values[] = new Type\Atomic\TLiteralString($current_dim->value); + $key_values[] = new TLiteralString($current_dim->value); } elseif ($current_dim instanceof PhpParser\Node\Scalar\LNumber) { - $key_values[] = new Type\Atomic\TLiteralInt($current_dim->value); + $key_values[] = new TLiteralInt($current_dim->value); } elseif ($current_dim && ($key_type = $statements_analyzer->node_data->getType($current_dim)) ) { @@ -863,13 +877,13 @@ private static function analyzeNestedArrayAssignment( ); } else { if (!$current_dim) { - $array_assignment_type = new Type\Union([ + $array_assignment_type = new Union([ new TList($current_type), ]); } else { $key_type = $statements_analyzer->node_data->getType($current_dim); - $array_assignment_type = new Type\Union([ + $array_assignment_type = new Union([ new TArray([ $key_type && !$key_type->hasMixed() ? $key_type @@ -924,12 +938,12 @@ private static function analyzeNestedArrayAssignment( } /** - * @return array{Type\Atomic\TLiteralInt|Type\Atomic\TLiteralString|null, string, bool} + * @return array{TLiteralInt|TLiteralString|null, string, bool} */ private static function getArrayAssignmentOffsetType( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\ArrayDimFetch $child_stmt, - Type\Union $child_stmt_dim_type + Union $child_stmt_dim_type ): array { if ($child_stmt->dim instanceof PhpParser\Node\Scalar\String_ || (($child_stmt->dim instanceof PhpParser\Node\Expr\ConstFetch @@ -937,7 +951,7 @@ private static function getArrayAssignmentOffsetType( && $child_stmt_dim_type->isSingleStringLiteral()) ) { if ($child_stmt->dim instanceof PhpParser\Node\Scalar\String_) { - $offset_type = new Type\Atomic\TLiteralString($child_stmt->dim->value); + $offset_type = new TLiteralString($child_stmt->dim->value); } else { $offset_type = $child_stmt_dim_type->getSingleStringLiteral(); } @@ -957,7 +971,7 @@ private static function getArrayAssignmentOffsetType( && $child_stmt_dim_type->isSingleIntLiteral()) ) { if ($child_stmt->dim instanceof PhpParser\Node\Scalar\LNumber) { - $offset_type = new Type\Atomic\TLiteralInt($child_stmt->dim->value); + $offset_type = new TLiteralInt($child_stmt->dim->value); } else { $offset_type = $child_stmt_dim_type->getSingleIntLiteral(); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php index 05c643c13fb..9756e4fc5f7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php @@ -1,12 +1,13 @@ property_type = $property_type; $this->id = $id; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php index 0c4e331242e..12a09c8d7b7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php @@ -1,4 +1,5 @@ data_flow_graph) { @@ -624,7 +630,7 @@ private static function analyzeRegularAssignment( Context $context, bool $direct_assignment, Codebase $codebase, - Type\Union $assignment_value_type, + Union $assignment_value_type, string $prop_name, ?string &$var_id ): array { @@ -747,7 +753,7 @@ private static function analyzeRegularAssignment( while ($lhs_atomic_types) { $lhs_type_part = array_pop($lhs_atomic_types); - if ($lhs_type_part instanceof Type\Atomic\TTemplateParam) { + if ($lhs_type_part instanceof TTemplateParam) { $lhs_atomic_types = array_merge( $lhs_atomic_types, $lhs_type_part->as->getAtomicTypes() @@ -844,11 +850,11 @@ private static function analyzeAtomicAssignment( ?PhpParser\Node\Expr $assignment_value, string $prop_name, Context $context, - Type\Union $lhs_type, - Type\Atomic $lhs_type_part, + Union $lhs_type, + Atomic $lhs_type_part, array &$invalid_assignment_types, ?string $var_id, - Type\Union $assignment_value_type, + Union $assignment_value_type, ?string $lhs_var_id, bool &$has_valid_assignment_type, bool &$has_regular_setter @@ -857,7 +863,7 @@ private static function analyzeAtomicAssignment( return null; } - if ($lhs_type_part instanceof Type\Atomic\TFalse + if ($lhs_type_part instanceof TFalse && $lhs_type->ignore_falsable_issues && count($lhs_type->getAtomicTypes()) > 1 ) { @@ -1350,7 +1356,7 @@ private static function analyzeAtomicAssignment( $declaring_property_class ); - if ($lhs_type_part instanceof Type\Atomic\TGenericObject) { + if ($lhs_type_part instanceof TGenericObject) { $class_property_type = AtomicPropertyFetchAnalyzer::localizePropertyType( $codebase, $class_property_type, @@ -1412,7 +1418,7 @@ public static function getExpandedPropertyType( string $fq_class_name, string $property_name, ClassLikeStorage $storage - ): ?Type\Union { + ): ?Union { $property_class_name = $codebase->properties->getDeclaringClassForProperty( $fq_class_name . '::$' . $property_name, true @@ -1450,7 +1456,7 @@ public static function getExpandedPropertyType( $property_class_storage, $storage, null, - new Type\Atomic\TNamedObject($fq_class_name), + new TNamedObject($fq_class_name), true ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/StaticPropertyAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/StaticPropertyAssignmentAnalyzer.php index 2ecf9f8480a..ebe454a968a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/StaticPropertyAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/StaticPropertyAssignmentAnalyzer.php @@ -1,4 +1,5 @@ name; foreach ($lhs_type->getAtomicTypes() as $lhs_atomic_type) { - if ($lhs_atomic_type instanceof Type\Atomic\TClassString) { + if ($lhs_atomic_type instanceof TClassString) { if (!$lhs_atomic_type->as_type) { continue; } @@ -66,7 +70,7 @@ public static function analyze( $lhs_atomic_type = $lhs_atomic_type->as_type; } - if (!$lhs_atomic_type instanceof Type\Atomic\TNamedObject) { + if (!$lhs_atomic_type instanceof TNamedObject) { continue; } @@ -78,6 +82,8 @@ public static function analyze( $context->inside_general_use = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $prop_name, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return false; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 6a714681295..f39bf696d80 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -1,8 +1,10 @@ inside_general_use = $was_inside_general_use; + if ($var_id) { if ($array_var_id) { $context->removeDescendents($array_var_id, null, $assign_value_type); @@ -539,9 +553,7 @@ public static function analyze( $context->vars_in_scope[$var_id] = Type::getNull(); - if (!$was_in_assignment) { - $context->inside_assignment = false; - } + $context->inside_assignment = $was_in_assignment; return $context->vars_in_scope[$var_id]; } @@ -559,9 +571,7 @@ public static function analyze( $context->vars_in_scope[$var_id] = Type::getEmpty(); - if (!$was_in_assignment) { - $context->inside_assignment = false; - } + $context->inside_assignment = $was_in_assignment; return $context->vars_in_scope[$var_id]; } @@ -600,9 +610,7 @@ public static function analyze( } } - if (!$was_in_assignment) { - $context->inside_assignment = false; - } + $context->inside_assignment = $was_in_assignment; return $assign_value_type; } @@ -613,8 +621,8 @@ public static function assignTypeFromVarDocblock( VarDocblockComment $var_comment, Context $context, ?string $var_id = null, - ?Type\Union &$comment_type = null, - ?CodeLocation\DocblockTypeLocation &$comment_type_location = null, + ?Union &$comment_type = null, + ?DocblockTypeLocation &$comment_type_location = null, array $not_ignored_docblock_var_ids = [] ): void { if (!$var_comment->type) { @@ -651,7 +659,7 @@ public static function assignTypeFromVarDocblock( && $var_comment->type_end && $var_comment->line_number ) { - $type_location = new CodeLocation\DocblockTypeLocation( + $type_location = new DocblockTypeLocation( $statements_analyzer, $var_comment->type_start, $var_comment->type_end, @@ -720,7 +728,7 @@ public static function assignTypeFromVarDocblock( * @param array $added_taints */ private static function taintAssignment( - Type\Union $type, + Union $type, DataFlowGraph $data_flow_graph, string $var_id, CodeLocation $var_location, @@ -918,8 +926,8 @@ public static function analyzeAssignmentRef( public static function assignByRefParam( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr $stmt, - Type\Union $by_ref_type, - Type\Union $by_ref_out_type, + Union $by_ref_type, + Union $by_ref_out_type, Context $context, bool $constrain_type = true, bool $prevent_null = false @@ -1055,7 +1063,7 @@ private static function analyzeDestructuringAssignment( Codebase $codebase, PhpParser\Node\Expr $assign_var, ?PhpParser\Node\Expr $assign_value, - Type\Union $assign_value_type, + Union $assign_value_type, Context $context, ?PhpParser\Comment\Doc $doc_comment, ?string $array_var_id, @@ -1119,7 +1127,7 @@ private static function analyzeDestructuringAssignment( $has_null = false; foreach ($assign_value_type->getAtomicTypes() as $assign_value_atomic_type) { - if ($assign_value_atomic_type instanceof Type\Atomic\TKeyedArray + if ($assign_value_atomic_type instanceof TKeyedArray && !$assign_var_item->key ) { // if object-like has int offsets @@ -1190,7 +1198,7 @@ private static function analyzeDestructuringAssignment( } } - if ($assign_value_atomic_type instanceof Type\Atomic\TMixed) { + if ($assign_value_atomic_type instanceof TMixed) { IssueBuffer::maybeAdd( new MixedArrayAccess( 'Cannot access array value on mixed variable ' . $array_var_id, @@ -1198,28 +1206,17 @@ private static function analyzeDestructuringAssignment( ), $statements_analyzer->getSuppressedIssues() ); - } elseif ($assign_value_atomic_type instanceof Type\Atomic\TNull) { + } elseif ($assign_value_atomic_type instanceof TNull) { $has_null = true; - - if (IssueBuffer::accepts( - new PossiblyNullArrayAccess( - 'Cannot access array value on null variable ' . $array_var_id, - new CodeLocation($statements_analyzer->getSource(), $var) - ), - $statements_analyzer->getSuppressedIssues() - ) - ) { - // do nothing - } - } elseif (!$assign_value_atomic_type instanceof Type\Atomic\TArray - && !$assign_value_atomic_type instanceof Type\Atomic\TKeyedArray - && !$assign_value_atomic_type instanceof Type\Atomic\TList + } elseif (!$assign_value_atomic_type instanceof TArray + && !$assign_value_atomic_type instanceof TKeyedArray + && !$assign_value_atomic_type instanceof TList && !$assign_value_type->hasArrayAccessInterface($codebase) ) { if ($assign_value_type->hasArray()) { - if (($assign_value_atomic_type instanceof Type\Atomic\TFalse + if (($assign_value_atomic_type instanceof TFalse && $assign_value_type->ignore_falsable_issues) - || ($assign_value_atomic_type instanceof Type\Atomic\TNull + || ($assign_value_atomic_type instanceof TNull && $assign_value_type->ignore_nullable_issues) ) { // do nothing @@ -1252,18 +1249,18 @@ private static function analyzeDestructuringAssignment( if ($var instanceof PhpParser\Node\Expr\List_ || $var instanceof PhpParser\Node\Expr\Array_ ) { - if ($assign_value_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($assign_value_atomic_type instanceof TKeyedArray) { $assign_value_atomic_type = $assign_value_atomic_type->getGenericArrayType(); } - if ($assign_value_atomic_type instanceof Type\Atomic\TList) { - $assign_value_atomic_type = new Type\Atomic\TArray([ + if ($assign_value_atomic_type instanceof TList) { + $assign_value_atomic_type = new TArray([ Type::getInt(), $assign_value_atomic_type->type_param ]); } - $array_value_type = $assign_value_atomic_type instanceof Type\Atomic\TArray + $array_value_type = $assign_value_atomic_type instanceof TArray ? clone $assign_value_atomic_type->type_params[1] : Type::getMixed(); @@ -1307,7 +1304,7 @@ private static function analyzeDestructuringAssignment( } } - if ($assign_value_atomic_type instanceof Type\Atomic\TArray) { + if ($assign_value_atomic_type instanceof TArray) { $new_assign_type = clone $assign_value_atomic_type->type_params[1]; if ($statements_analyzer->data_flow_graph @@ -1322,8 +1319,8 @@ private static function analyzeDestructuringAssignment( ); } - $can_be_empty = !$assign_value_atomic_type instanceof Type\Atomic\TNonEmptyArray; - } elseif ($assign_value_atomic_type instanceof Type\Atomic\TList) { + $can_be_empty = !$assign_value_atomic_type instanceof TNonEmptyArray; + } elseif ($assign_value_atomic_type instanceof TList) { $new_assign_type = clone $assign_value_atomic_type->type_param; if ($statements_analyzer->data_flow_graph && $assign_value) { @@ -1336,8 +1333,8 @@ private static function analyzeDestructuringAssignment( ); } - $can_be_empty = !$assign_value_atomic_type instanceof Type\Atomic\TNonEmptyList; - } elseif ($assign_value_atomic_type instanceof Type\Atomic\TKeyedArray) { + $can_be_empty = !$assign_value_atomic_type instanceof TNonEmptyList; + } elseif ($assign_value_atomic_type instanceof TKeyedArray) { if (($assign_var_item->key instanceof PhpParser\Node\Scalar\String_ || $assign_var_item->key instanceof PhpParser\Node\Scalar\LNumber) && isset($assign_value_atomic_type->properties[$assign_var_item->key->value]) @@ -1392,7 +1389,19 @@ private static function analyzeDestructuringAssignment( } } + + if (!$assigned) { + if ($has_null) { + IssueBuffer::maybeAdd( + new PossiblyNullArrayAccess( + 'Cannot access array value on null variable ' . $array_var_id, + new CodeLocation($statements_analyzer->getSource(), $var) + ), + $statements_analyzer->getSuppressedIssues() + ); + } + foreach ($var_comments as $var_comment) { if (!$var_comment->type) { continue; @@ -1426,12 +1435,6 @@ private static function analyzeDestructuringAssignment( if ($list_var_id) { $context->vars_in_scope[$list_var_id] = $new_assign_type ?: Type::getMixed(); - if (($context->error_suppressing && ($offset || $can_be_empty)) - || $has_null - ) { - $context->vars_in_scope[$list_var_id]->addType(new Type\Atomic\TNull); - } - if ($statements_analyzer->data_flow_graph) { $data_flow_graph = $statements_analyzer->data_flow_graph; @@ -1473,6 +1476,14 @@ private static function analyzeDestructuringAssignment( } } } + + if ($list_var_id) { + if (($context->error_suppressing && ($offset || $can_be_empty)) + || $has_null + ) { + $context->vars_in_scope[$list_var_id]->addType(new TNull); + } + } } } @@ -1482,7 +1493,7 @@ private static function analyzePropertyAssignment( PhpParser\Node\Expr\PropertyFetch $assign_var, Context $context, ?PhpParser\Node\Expr $assign_value, - Type\Union $assign_value_type, + Union $assign_value_type, ?string $var_id ): void { if (!$assign_var->name instanceof PhpParser\Node\Identifier) { @@ -1492,10 +1503,14 @@ private static function analyzePropertyAssignment( // this can happen when the user actually means to type $this->, but there's // a variable on the next line if (ExpressionAnalyzer::analyze($statements_analyzer, $assign_var->var, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return; } if (ExpressionAnalyzer::analyze($statements_analyzer, $assign_var->name, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return; } @@ -1533,7 +1548,7 @@ private static function analyzePropertyAssignment( if ($stmt_var_type->hasObjectType()) { foreach ($stmt_var_type->getAtomicTypes() as $type) { - if ($type instanceof Type\Atomic\TNamedObject) { + if ($type instanceof TNamedObject) { $codebase->analyzer->addMixedMemberName( strtolower($type->value) . '::$', $context->calling_method_id ?: $statements_analyzer->getFileName() @@ -1582,7 +1597,7 @@ private static function analyzeAssignmentToVariable( Codebase $codebase, PhpParser\Node\Expr\Variable $assign_var, ?PhpParser\Node\Expr $assign_value, - Type\Union $assign_value_type, + Union $assign_value_type, ?string $var_id, Context $context ): void { @@ -1684,6 +1699,8 @@ private static function analyzeAssignmentToVariable( $context->inside_general_use = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $assign_var->name, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php index c15606ee2b8..1f4c28f1b42 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php @@ -1,4 +1,5 @@ getCodebase() : null; @@ -275,7 +280,7 @@ public static function analyze( /** * @param int|float $result */ - private static function getNumericalType($result): Type\Union + private static function getNumericalType($result): Union { if (is_int($result)) { return Type::getInt(false, $result); @@ -296,15 +301,15 @@ private static function analyzeOperands( PhpParser\Node\Expr $left, PhpParser\Node\Expr $right, PhpParser\Node $parent, - Type\Atomic $left_type_part, - Type\Atomic $right_type_part, + Atomic $left_type_part, + Atomic $right_type_part, array &$invalid_left_messages, array &$invalid_right_messages, bool &$has_valid_left_operand, bool &$has_valid_right_operand, bool &$has_string_increment, - Type\Union &$result_type = null - ): ?Type\Union { + Union &$result_type = null + ): ?Union { if ($left_type_part instanceof TLiteralInt && $right_type_part instanceof TLiteralInt && ( @@ -345,17 +350,17 @@ private static function analyzeOperands( return null; } - if ($left_type_part instanceof Type\Atomic\TString + if ($left_type_part instanceof TString && $right_type_part instanceof TInt && ( $parent instanceof PhpParser\Node\Expr\PostInc || $parent instanceof PhpParser\Node\Expr\PreInc ) ) { - if ($left_type_part instanceof Type\Atomic\TNumericString || - ($left_type_part instanceof Type\Atomic\TLiteralString && is_numeric($left_type_part->value)) + if ($left_type_part instanceof TNumericString || + ($left_type_part instanceof TLiteralString && is_numeric($left_type_part->value)) ) { - $new_result_type = new Type\Union([new TFloat(), new TInt()]); + $new_result_type = new Union([new TFloat(), new TInt()]); $new_result_type->from_calculation = true; } else { $new_result_type = Type::getNonEmptyString(); @@ -426,7 +431,7 @@ private static function analyzeOperands( && $parent instanceof PhpParser\Node\Expr\AssignOp\Plus && !$right_type_part instanceof TMixed ) { - $result_type = Type::combineUnionTypes(new Type\Union([$right_type_part]), $result_type); + $result_type = Type::combineUnionTypes(new Union([$right_type_part]), $result_type); return null; } @@ -552,7 +557,7 @@ private static function analyzeOperands( $new_keyed_array = new TKeyedArray($properties); $new_keyed_array->sealed = $left_type_part->sealed && $right_type_part->sealed; - $result_type_member = new Type\Union([$new_keyed_array]); + $result_type_member = new Union([$new_keyed_array]); } else { $result_type_member = TypeCombiner::combine( [$left_type_part, $right_type_part], @@ -594,7 +599,7 @@ private static function analyzeOperands( || ($left_type_part->isNumericType() || $left_type_part instanceof TMixed))) ) { $result_type = Type::combineUnionTypes( - new Type\Union([new TNamedObject('GMP')]), + new Union([new TNamedObject('GMP')]), $result_type ); } else { @@ -636,7 +641,7 @@ private static function analyzeOperands( && strtolower($non_decimal_type->value) === "decimal\\decimal" ) { $result_type = Type::combineUnionTypes( - new Type\Union([new TNamedObject("Decimal\\Decimal")]), + new Union([new TNamedObject("Decimal\\Decimal")]), $result_type ); } else { @@ -655,19 +660,19 @@ private static function analyzeOperands( } } - if ($left_type_part instanceof Type\Atomic\TLiteralString) { + if ($left_type_part instanceof TLiteralString) { if (preg_match('/^\-?\d+$/', $left_type_part->value)) { - $left_type_part = new Type\Atomic\TLiteralInt((int) $left_type_part->value); + $left_type_part = new TLiteralInt((int) $left_type_part->value); } elseif (preg_match('/^\-?\d?\.\d+$/', $left_type_part->value)) { - $left_type_part = new Type\Atomic\TLiteralFloat((float) $left_type_part->value); + $left_type_part = new TLiteralFloat((float) $left_type_part->value); } } - if ($right_type_part instanceof Type\Atomic\TLiteralString) { + if ($right_type_part instanceof TLiteralString) { if (preg_match('/^\-?\d+$/', $right_type_part->value)) { - $right_type_part = new Type\Atomic\TLiteralInt((int) $right_type_part->value); + $right_type_part = new TLiteralInt((int) $right_type_part->value); } elseif (preg_match('/^\-?\d?\.\d+$/', $right_type_part->value)) { - $right_type_part = new Type\Atomic\TLiteralFloat((float) $right_type_part->value); + $right_type_part = new TLiteralFloat((float) $right_type_part->value); } } @@ -691,7 +696,7 @@ private static function analyzeOperands( if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) { $new_result_type = Type::getInt(); } else { - $new_result_type = new Type\Union([new TFloat(), new TInt()]); + $new_result_type = new Union([new TFloat(), new TInt()]); } $result_type = Type::combineUnionTypes($new_result_type, $result_type); @@ -702,13 +707,13 @@ private static function analyzeOperands( return null; } - if ($left_type_part instanceof Type\Atomic\TIntRange && $right_type_part instanceof Type\Atomic\TIntRange) { + if ($left_type_part instanceof TIntRange && $right_type_part instanceof TIntRange) { self::analyzeOperandsBetweenIntRange($parent, $result_type, $left_type_part, $right_type_part); return null; } - if (($left_type_part instanceof Type\Atomic\TIntRange && $right_type_part instanceof TInt) || - ($left_type_part instanceof TInt && $right_type_part instanceof Type\Atomic\TIntRange) + if (($left_type_part instanceof TIntRange && $right_type_part instanceof TInt) || + ($left_type_part instanceof TInt && $right_type_part instanceof TIntRange) ) { self::analyzeOperandsBetweenIntRangeAndInt( $parent, @@ -721,7 +726,7 @@ private static function analyzeOperands( if ($left_type_part instanceof TInt && $right_type_part instanceof TInt) { if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Div) { - $result_type = new Type\Union([new Type\Atomic\TInt(), new Type\Atomic\TFloat()]); + $result_type = new Union([new TInt(), new TFloat()]); } else { $left_is_positive = $left_type_part instanceof TPositiveInt || ($left_type_part instanceof TLiteralInt && $left_type_part->value > 0); @@ -759,16 +764,16 @@ private static function analyzeOperands( if ($right_type_part instanceof TLiteralInt) { $literal_value_max = $right_type_part->value - 1; if ($always_positive) { - $result_type = new Type\Union([new Type\Atomic\TIntRange(0, $literal_value_max)]); + $result_type = new Union([new TIntRange(0, $literal_value_max)]); } else { - $result_type = new Type\Union( - [new Type\Atomic\TIntRange(-$literal_value_max, $literal_value_max)] + $result_type = new Union( + [new TIntRange(-$literal_value_max, $literal_value_max)] ); } } else { if ($always_positive) { - $result_type = new Type\Union([ - new Type\Atomic\TPositiveInt(), + $result_type = new Union([ + new TPositiveInt(), new TLiteralInt(0) ]); } else { @@ -847,7 +852,7 @@ private static function analyzeOperands( if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) { $result_type = Type::getInt(); } else { - $result_type = new Type\Union([new Type\Atomic\TInt, new Type\Atomic\TFloat]); + $result_type = new Union([new TInt, new TFloat]); } $has_valid_right_operand = true; @@ -884,7 +889,7 @@ public static function arithmeticOperation( $operand1, $operand2, bool $allow_float_result - ): ?Type\Union { + ): ?Union { if ($operation instanceof PhpParser\Node\Expr\BinaryOp\Plus) { $result = $operand1 + $operand2; } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Minus) { @@ -929,14 +934,14 @@ public static function arithmeticOperation( private static function analyzeOperandsBetweenIntRange( PhpParser\Node $parent, - ?Type\Union &$result_type, + ?Union &$result_type, TIntRange $left_type_part, TIntRange $right_type_part ): void { if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Div) { //can't assume an int range will stay int after division $result_type = Type::combineUnionTypes( - new Type\Union([new Type\Atomic\TInt(), new Type\Atomic\TFloat()]), + new Union([new TInt(), new TFloat()]), $result_type ); return; @@ -964,7 +969,7 @@ private static function analyzeOperandsBetweenIntRange( ) { //really complex to calculate $result_type = Type::combineUnionTypes( - new Type\Union([new Type\Atomic\TInt()]), + new Union([new TInt()]), $result_type ); return; @@ -1020,7 +1025,7 @@ private static function analyzeOperandsBetweenIntRange( $min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null; $max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null; - $new_result_type = new Type\Union([new Type\Atomic\TIntRange($min_value, $max_value)]); + $new_result_type = new Union([new TIntRange($min_value, $max_value)]); $result_type = Type::combineUnionTypes($new_result_type, $result_type); } @@ -1031,14 +1036,14 @@ private static function analyzeOperandsBetweenIntRange( */ private static function analyzeOperandsBetweenIntRangeAndInt( PhpParser\Node $parent, - ?Type\Union &$result_type, + ?Union &$result_type, Atomic $left_type_part, Atomic $right_type_part ): void { - if (!$left_type_part instanceof Type\Atomic\TIntRange) { + if (!$left_type_part instanceof TIntRange) { $left_type_part = TIntRange::convertToIntRange($left_type_part); } - if (!$right_type_part instanceof Type\Atomic\TIntRange) { + if (!$right_type_part instanceof TIntRange) { $right_type_part = TIntRange::convertToIntRange($right_type_part); } @@ -1047,7 +1052,7 @@ private static function analyzeOperandsBetweenIntRangeAndInt( private static function analyzeMulBetweenIntRange( PhpParser\Node\Expr\BinaryOp\Mul $parent, - ?Type\Union &$result_type, + ?Union &$result_type, TIntRange $left_type_part, TIntRange $right_type_part ): void { @@ -1070,7 +1075,7 @@ private static function analyzeMulBetweenIntRange( $min_value = min($x_1 * $y_1, $x_1 * $y_2, $x_2 * $y_1, $x_2 * $y_2); $max_value = max($x_1 * $y_1, $x_1 * $y_2, $x_2 * $y_1, $x_2 * $y_2); - $new_result_type = new Type\Union([new TIntRange($min_value, $max_value)]); + $new_result_type = new Union([new TIntRange($min_value, $max_value)]); } elseif ($right_type_part->isPositiveOrZero() && $left_type_part->isPositiveOrZero()) { // both operands are positive, result will be only positive $min_operand1 = $left_type_part->min_bound; @@ -1104,7 +1109,7 @@ private static function analyzeMulBetweenIntRange( $min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null; $max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null; - $new_result_type = new Type\Union([new Type\Atomic\TIntRange($min_value, $max_value)]); + $new_result_type = new Union([new TIntRange($min_value, $max_value)]); } elseif ($right_type_part->isPositiveOrZero() && $left_type_part->isNegativeOrZero()) { // one operand is negative, result will be negative and we have to check min vs max $min_operand1 = $left_type_part->max_bound; @@ -1142,7 +1147,7 @@ private static function analyzeMulBetweenIntRange( [$min_value, $max_value] = [$max_value, $min_value]; } - $new_result_type = new Type\Union([new Type\Atomic\TIntRange($min_value, $max_value)]); + $new_result_type = new Union([new TIntRange($min_value, $max_value)]); } elseif ($right_type_part->isNegativeOrZero() && $left_type_part->isPositiveOrZero()) { // one operand is negative, result will be negative and we have to check min vs max $min_operand1 = $left_type_part->min_bound; @@ -1180,7 +1185,7 @@ private static function analyzeMulBetweenIntRange( [$min_value, $max_value] = [$max_value, $min_value]; } - $new_result_type = new Type\Union([new Type\Atomic\TIntRange($min_value, $max_value)]); + $new_result_type = new Union([new TIntRange($min_value, $max_value)]); } elseif ($right_type_part->isNegativeOrZero() && $left_type_part->isNegativeOrZero()) { // both operand are negative, result will be positive $min_operand1 = $left_type_part->max_bound; @@ -1214,7 +1219,7 @@ private static function analyzeMulBetweenIntRange( $min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null; $max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null; - $new_result_type = new Type\Union([new Type\Atomic\TIntRange($min_value, $max_value)]); + $new_result_type = new Union([new TIntRange($min_value, $max_value)]); } else { $new_result_type = Type::getInt(true); } @@ -1223,7 +1228,7 @@ private static function analyzeMulBetweenIntRange( } private static function analyzePowBetweenIntRange( - ?Type\Union &$result_type, + ?Union &$result_type, TIntRange $left_type_part, TIntRange $right_type_part ): void { @@ -1231,22 +1236,22 @@ private static function analyzePowBetweenIntRange( //If Pow second operand is negative, the result will be float, if it's 0, it will be 1/-1, else positive if ($left_type_part->isPositive()) { if ($right_type_part->isPositive()) { - $new_result_type = new Type\Union([new TIntRange(1, null)]); + $new_result_type = new Union([new TIntRange(1, null)]); } elseif ($right_type_part->isNegative()) { $new_result_type = Type::getFloat(); } elseif ($right_type_part->min_bound === 0 && $right_type_part->max_bound === 0) { $new_result_type = Type::getInt(true, 1); } else { //$right_type_part may be a mix of positive, negative and 0 - $new_result_type = new Type\Union([new TInt(), new TFloat()]); + $new_result_type = new Union([new TInt(), new TFloat()]); } } elseif ($left_type_part->isNegative()) { if ($right_type_part->isPositive()) { if ($right_type_part->min_bound === $right_type_part->max_bound) { if ($right_type_part->max_bound % 2 === 0) { - $new_result_type = new Type\Union([new TIntRange(1, null)]); + $new_result_type = new Union([new TIntRange(1, null)]); } else { - $new_result_type = new Type\Union([new TIntRange(null, -1)]); + $new_result_type = new Union([new TIntRange(null, -1)]); } } else { $new_result_type = Type::getInt(true); @@ -1257,7 +1262,7 @@ private static function analyzePowBetweenIntRange( $new_result_type = Type::getInt(true, -1); } else { //$right_type_part may be a mix of positive, negative and 0 - $new_result_type = new Type\Union([new TInt(), new TFloat()]); + $new_result_type = new Union([new TInt(), new TFloat()]); } } elseif ($left_type_part->min_bound === 0 && $left_type_part->max_bound === 0) { if ($right_type_part->isPositive()) { @@ -1274,7 +1279,7 @@ private static function analyzePowBetweenIntRange( if ($right_type_part->min_bound === $right_type_part->max_bound && $right_type_part->max_bound % 2 === 0 ) { - $new_result_type = new Type\Union([new TIntRange(1, null)]); + $new_result_type = new Union([new TIntRange(1, null)]); } else { $new_result_type = Type::getInt(true); } @@ -1284,7 +1289,7 @@ private static function analyzePowBetweenIntRange( $new_result_type = Type::getInt(true, 1); } else { //$left_type_part may be a mix of positive, negative and 0 - $new_result_type = new Type\Union([new TInt(), new TFloat()]); + $new_result_type = new Union([new TInt(), new TFloat()]); } } @@ -1292,7 +1297,7 @@ private static function analyzePowBetweenIntRange( } private static function analyzeModBetweenIntRange( - ?Type\Union &$result_type, + ?Union &$result_type, TIntRange $left_type_part, TIntRange $right_type_part ): void { @@ -1305,18 +1310,18 @@ private static function analyzeModBetweenIntRange( if ($left_type_part->isPositiveOrZero()) { if ($right_type_part->isPositive()) { $max = $right_type_part->min_bound - 1; - $new_result_type = new Type\Union([new TIntRange(0, $max)]); + $new_result_type = new Union([new TIntRange(0, $max)]); } else { $max = $right_type_part->min_bound + 1; - $new_result_type = new Type\Union([new TIntRange($max, 0)]); + $new_result_type = new Union([new TIntRange($max, 0)]); } } elseif ($left_type_part->isNegativeOrZero()) { if ($right_type_part->isPositive()) { $max = $right_type_part->min_bound - 1; - $new_result_type = new Type\Union([new TIntRange(-$max, 0)]); + $new_result_type = new Union([new TIntRange(-$max, 0)]); } else { $max = $right_type_part->min_bound + 1; - $new_result_type = new Type\Union([new TIntRange(-$max, 0)]); + $new_result_type = new Union([new TIntRange(-$max, 0)]); } } else { if ($right_type_part->isPositive()) { @@ -1324,29 +1329,29 @@ private static function analyzeModBetweenIntRange( } else { $max = -$right_type_part->min_bound - 1; } - $new_result_type = new Type\Union([new TIntRange(-$max, $max)]); + $new_result_type = new Union([new TIntRange(-$max, $max)]); } } } elseif ($right_type_part->isPositive()) { if ($left_type_part->isPositiveOrZero()) { if ($right_type_part->max_bound !== null) { //we now that the result will be a range between 0 and $right->max - 1 - $new_result_type = new Type\Union( + $new_result_type = new Union( [new TIntRange(0, $right_type_part->max_bound - 1)] ); } else { - $new_result_type = new Type\Union([new TIntRange(0, null)]); + $new_result_type = new Union([new TIntRange(0, null)]); } } elseif ($left_type_part->isNegativeOrZero()) { - $new_result_type = new Type\Union([new TIntRange(null, 0)]); + $new_result_type = new Union([new TIntRange(null, 0)]); } else { $new_result_type = Type::getInt(true); } } elseif ($right_type_part->isNegative()) { if ($left_type_part->isPositiveOrZero()) { - $new_result_type = new Type\Union([new TIntRange(null, 0)]); + $new_result_type = new Union([new TIntRange(null, 0)]); } elseif ($left_type_part->isNegativeOrZero()) { - $new_result_type = new Type\Union([new TIntRange(null, 0)]); + $new_result_type = new Union([new TIntRange(null, 0)]); } else { $new_result_type = Type::getInt(true); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/CoalesceAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/CoalesceAnalyzer.php index ed1501b3804..f5c7580791d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/CoalesceAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/CoalesceAnalyzer.php @@ -1,4 +1,5 @@ getCodebase(); @@ -164,15 +176,15 @@ public static function analyze( break 2; } - $result_type_parts[] = new Type\Atomic\TLiteralString($literal); + $result_type_parts[] = new TLiteralString($literal); } } if (!empty($result_type_parts)) { if ($literal_concat && count($result_type_parts) < 64) { - $result_type = new Type\Union($result_type_parts); + $result_type = new Union($result_type_parts); } else { - $result_type = new Type\Union([new Type\Atomic\TNonEmptyNonspecificLiteralString]); + $result_type = new Union([new TNonEmptyNonspecificLiteralString]); } return; @@ -181,8 +193,8 @@ public static function analyze( if (!$literal_concat) { $numeric_type = Type::getNumericString(); - $numeric_type->addType(new Type\Atomic\TInt()); - $numeric_type->addType(new Type\Atomic\TFloat()); + $numeric_type->addType(new TInt()); + $numeric_type->addType(new TFloat()); $left_is_numeric = UnionTypeComparator::isContainedBy( $codebase, $left_type, @@ -191,7 +203,7 @@ public static function analyze( if ($left_is_numeric) { $right_uint = Type::getPositiveInt(); - $right_uint->addType(new Type\Atomic\TLiteralInt(0)); + $right_uint->addType(new TLiteralInt(0)); $right_is_uint = UnionTypeComparator::isContainedBy( $codebase, $right_type, @@ -205,7 +217,7 @@ public static function analyze( } $lowercase_type = clone $numeric_type; - $lowercase_type->addType(new Type\Atomic\TLowercaseString()); + $lowercase_type->addType(new TLowercaseString()); $all_lowercase = UnionTypeComparator::isContainedBy( $codebase, @@ -218,7 +230,7 @@ public static function analyze( ); $non_empty_string = clone $numeric_type; - $non_empty_string->addType(new Type\Atomic\TNonEmptyString()); + $non_empty_string->addType(new TNonEmptyString()); $has_non_empty = UnionTypeComparator::isContainedBy( $codebase, @@ -234,7 +246,7 @@ public static function analyze( if ($has_non_empty) { if ($all_literals) { - $result_type = new Type\Union([new Type\Atomic\TNonEmptyNonspecificLiteralString]); + $result_type = new Union([new TNonEmptyNonspecificLiteralString]); } elseif ($all_lowercase) { $result_type = Type::getNonEmptyLowercaseString(); } else { @@ -242,7 +254,7 @@ public static function analyze( } } else { if ($all_literals) { - $result_type = new Type\Union([new Type\Atomic\TNonspecificLiteralString]); + $result_type = new Union([new TNonspecificLiteralString]); } elseif ($all_lowercase) { $result_type = Type::getLowercaseString(); } else { @@ -256,7 +268,7 @@ public static function analyze( private static function analyzeOperand( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr $operand, - Type\Union $operand_type, + Union $operand_type, string $side, Context $context ): void { @@ -312,7 +324,7 @@ private static function analyzeOperand( $comparison_result = new TypeComparisonResult(); foreach ($operand_type->getAtomicTypes() as $operand_type_part) { - if ($operand_type_part instanceof Type\Atomic\TTemplateParam && !$operand_type_part->as->isString()) { + if ($operand_type_part instanceof TTemplateParam && !$operand_type_part->as->isString()) { IssueBuffer::maybeAdd( new MixedOperand( "$side operand cannot be a non-string template param", @@ -324,14 +336,14 @@ private static function analyzeOperand( return; } - if ($operand_type_part instanceof Type\Atomic\TNull || $operand_type_part instanceof Type\Atomic\TFalse) { + if ($operand_type_part instanceof TNull || $operand_type_part instanceof TFalse) { continue; } $operand_type_part_match = AtomicTypeComparator::isContainedBy( $codebase, $operand_type_part, - new Type\Atomic\TString, + new TString, false, false, $comparison_result diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonComparisonOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonComparisonOpAnalyzer.php index 9f7d678015f..1f99b1d9d67 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonComparisonOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonComparisonOpAnalyzer.php @@ -1,4 +1,5 @@ node_data->setType($stmt, $result_type); @@ -127,7 +131,7 @@ public static function analyze( ); if (!$result_type) { - $result_type = new Type\Union([new Type\Atomic\TInt(), new Type\Atomic\TFloat()]); + $result_type = new Union([new TInt(), new TFloat()]); } $statements_analyzer->node_data->setType($stmt, $result_type); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php index a71bd2e0aa9..d584d43f69d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php @@ -1,4 +1,5 @@ inside_general_use; $context->inside_general_use = true; - $expr_result = BinaryOp\AndAnalyzer::analyze( + $expr_result = AndAnalyzer::analyze( $statements_analyzer, $stmt, $context, @@ -71,7 +80,7 @@ public static function analyze( $was_inside_general_use = $context->inside_general_use; $context->inside_general_use = true; - $expr_result = BinaryOp\OrAnalyzer::analyze( + $expr_result = OrAnalyzer::analyze( $statements_analyzer, $stmt, $context, @@ -86,7 +95,7 @@ public static function analyze( } if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Coalesce) { - $expr_result = BinaryOp\CoalesceAnalyzer::analyze( + $expr_result = CoalesceAnalyzer::analyze( $statements_analyzer, $stmt, $context @@ -126,7 +135,7 @@ public static function analyze( if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat) { $stmt_type = Type::getString(); - BinaryOp\ConcatAnalyzer::analyze( + ConcatAnalyzer::analyze( $statements_analyzer, $stmt->left, $stmt->right, @@ -193,11 +202,11 @@ public static function analyze( if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Spaceship) { $statements_analyzer->node_data->setType( $stmt, - new Type\Union( + new Union( [ - new Type\Atomic\TLiteralInt(-1), - new Type\Atomic\TLiteralInt(0), - new Type\Atomic\TLiteralInt(1) + new TLiteralInt(-1), + new TLiteralInt(0), + new TLiteralInt(1) ] ) ); @@ -273,7 +282,7 @@ public static function analyze( if ($string_length > 0) { foreach ($stmt_right_type->getAtomicTypes() as $atomic_right_type) { - if ($atomic_right_type instanceof Type\Atomic\TLiteralString) { + if ($atomic_right_type instanceof TLiteralString) { if (strlen($atomic_right_type->value) !== $string_length) { if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical @@ -350,7 +359,7 @@ public static function analyze( return true; } - BinaryOp\NonComparisonOpAnalyzer::analyze( + NonComparisonOpAnalyzer::analyze( $statements_analyzer, $stmt, $context @@ -444,8 +453,8 @@ public static function addDataFlow( private static function checkForImpureEqualityComparison( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\BinaryOp\Equal $stmt, - Type\Union $stmt_left_type, - Type\Union $stmt_right_type + Union $stmt_left_type, + Union $stmt_right_type ): void { $codebase = $statements_analyzer->getCodebase(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php index 59324f54ddb..fa8c19d6ce6 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php @@ -15,7 +15,11 @@ use Psalm\Type; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; +use Psalm\Type\Atomic\TLiteralFloat; +use Psalm\Type\Atomic\TLiteralInt; +use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TString; +use Psalm\Type\Union; class BitwiseNotAnalyzer { @@ -29,7 +33,7 @@ public static function analyze( } if (!($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr))) { - $statements_analyzer->node_data->setType($stmt, new Type\Union([new TInt(), new TString()])); + $statements_analyzer->node_data->setType($stmt, new Union([new TInt(), new TString()])); } elseif ($stmt_expr_type->isMixed()) { $statements_analyzer->node_data->setType($stmt, Type::getMixed()); } else { @@ -39,17 +43,17 @@ public static function analyze( foreach ($stmt_expr_type->getAtomicTypes() as $type_string => $type_part) { if ($type_part instanceof TInt || $type_part instanceof TString) { - if ($type_part instanceof Type\Atomic\TLiteralInt) { + if ($type_part instanceof TLiteralInt) { $type_part->value = ~$type_part->value; - } elseif ($type_part instanceof Type\Atomic\TLiteralString) { + } elseif ($type_part instanceof TLiteralString) { $type_part->value = ~$type_part->value; } $acceptable_types[] = $type_part; $has_valid_operand = true; } elseif ($type_part instanceof TFloat) { - $type_part = ($type_part instanceof Type\Atomic\TLiteralFloat) ? - new Type\Atomic\TLiteralInt(~$type_part->value) : + $type_part = ($type_part instanceof TLiteralFloat) ? + new TLiteralInt(~$type_part->value) : new TInt; $stmt_expr_type->removeType($type_string); @@ -84,7 +88,7 @@ public static function analyze( $statements_analyzer->node_data->setType($stmt, Type::getMixed()); } else { - $statements_analyzer->node_data->setType($stmt, new Type\Union($acceptable_types)); + $statements_analyzer->node_data->setType($stmt, new Union($acceptable_types)); } } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php index 65ef31f3ca9..cf135ee9d91 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php @@ -1,4 +1,5 @@ > $class_generic_params + * @param array> $class_generic_params * @return false|null */ public static function checkArgumentMatches( @@ -82,7 +94,7 @@ public static function checkArgumentMatches( int $unpacked_argument_offset, bool $allow_named_args, PhpParser\Node\Arg $arg, - ?Type\Union $arg_value_type, + ?Union $arg_value_type, Context $context, array $class_generic_params, ?TemplateResult $template_result, @@ -111,11 +123,11 @@ public static function checkArgumentMatches( ) { /** * @psalm-suppress PossiblyUndefinedStringArrayOffset - * @var Atomic\TList|Atomic\TArray + * @var TList|TArray */ $array_type = $param_type->getAtomicTypes()['array']; - if ($array_type instanceof Atomic\TList) { + if ($array_type instanceof TList) { $param_type = $array_type->type_param; } else { $param_type = $array_type->type_params[1]; @@ -206,7 +218,7 @@ public static function checkArgumentMatches( } /** - * @param array> $class_generic_params + * @param array> $class_generic_params * @return false|null */ private static function checkFunctionLikeTypeMatches( @@ -219,7 +231,7 @@ private static function checkFunctionLikeTypeMatches( CodeLocation $function_call_location, FunctionLikeParameter $function_param, bool $allow_named_args, - Type\Union $arg_type, + Union $arg_type, int $argument_offset, int $unpacked_argument_offset, PhpParser\Node\Arg $arg, @@ -320,20 +332,20 @@ private static function checkFunctionLikeTypeMatches( $arg_type_param = null; foreach ($arg_type->getAtomicTypes() as $arg_atomic_type) { - if ($arg_atomic_type instanceof Atomic\TArray - || $arg_atomic_type instanceof Atomic\TList - || $arg_atomic_type instanceof Atomic\TKeyedArray + if ($arg_atomic_type instanceof TArray + || $arg_atomic_type instanceof TList + || $arg_atomic_type instanceof TKeyedArray ) { - if ($arg_atomic_type instanceof Atomic\TKeyedArray) { + if ($arg_atomic_type instanceof TKeyedArray) { $arg_type_param = $arg_atomic_type->getGenericValueType(); - } elseif ($arg_atomic_type instanceof Atomic\TList) { + } elseif ($arg_atomic_type instanceof TList) { $arg_type_param = $arg_atomic_type->type_param; } else { $arg_type_param = $arg_atomic_type->type_params[1]; } - } elseif ($arg_atomic_type instanceof Atomic\TIterable) { + } elseif ($arg_atomic_type instanceof TIterable) { $arg_type_param = $arg_atomic_type->type_params[1]; - } elseif ($arg_atomic_type instanceof Atomic\TNamedObject) { + } elseif ($arg_atomic_type instanceof TNamedObject) { ForeachAnalyzer::getKeyValueParamsForTraversableObject( $arg_atomic_type, $codebase, @@ -462,12 +474,12 @@ private static function checkFunctionLikeTypeMatches( if ($arg_type->hasArray()) { /** * @psalm-suppress PossiblyUndefinedStringArrayOffset - * @var Atomic\TArray|Atomic\TList|Atomic\TKeyedArray|Atomic\TClassStringMap + * @var TArray|TList|TKeyedArray|TClassStringMap */ $unpacked_atomic_array = $arg_type->getAtomicTypes()['array']; $arg_key_allowed = true; - if ($unpacked_atomic_array instanceof Atomic\TKeyedArray) { + if ($unpacked_atomic_array instanceof TKeyedArray) { if (!$allow_named_args && !$unpacked_atomic_array->getGenericKeyType()->isInt()) { $arg_key_allowed = false; } @@ -484,7 +496,7 @@ private static function checkFunctionLikeTypeMatches( ) { $arg_type = clone $unpacked_atomic_array->properties[$unpacked_argument_offset]; } elseif ($function_param->is_optional && $function_param->default_type) { - if ($function_param->default_type instanceof Type\Union) { + if ($function_param->default_type instanceof Union) { $arg_type = $function_param->default_type; } else { $arg_type_atomic = ConstantTypeResolver::resolve( @@ -493,14 +505,14 @@ private static function checkFunctionLikeTypeMatches( $statements_analyzer ); - $arg_type = new Type\Union([$arg_type_atomic]); + $arg_type = new Union([$arg_type_atomic]); } } else { $arg_type = Type::getMixed(); } - } elseif ($unpacked_atomic_array instanceof Atomic\TList) { + } elseif ($unpacked_atomic_array instanceof TList) { $arg_type = $unpacked_atomic_array->type_param; - } elseif ($unpacked_atomic_array instanceof Atomic\TClassStringMap) { + } elseif ($unpacked_atomic_array instanceof TClassStringMap) { $arg_type = Type::getMixed(); } else { if (!$allow_named_args && !$unpacked_atomic_array->type_params[0]->isInt()) { @@ -646,15 +658,15 @@ private static function checkFunctionLikeTypeMatches( } /** - * @param Atomic\TKeyedArray|Atomic\TArray|Atomic\TList|Atomic\TClassStringMap $unpacked_atomic_array + * @param TKeyedArray|TArray|TList|TClassStringMap $unpacked_atomic_array * @return null|false * @psalm-suppress ComplexMethod */ public static function verifyType( StatementsAnalyzer $statements_analyzer, - Type\Union $input_type, - Type\Union $param_type, - ?Type\Union $signature_param_type, + Union $input_type, + Union $param_type, + ?Union $signature_param_type, ?string $cased_method_id, ?MethodIdentifier $method_id, int $argument_offset, @@ -663,7 +675,7 @@ public static function verifyType( Context $context, FunctionLikeParameter $function_param, bool $unpack, - ?Type\Atomic $unpacked_atomic_array, + ?Atomic $unpacked_atomic_array, bool $specialize_taint, bool $in_call_map, CodeLocation $function_call_location @@ -825,7 +837,7 @@ public static function verifyType( // we do this replacement early because later we don't have access to the // $statements_analyzer, which is necessary to understand string function names foreach ($input_type->getAtomicTypes() as $key => $atomic_type) { - if (!$atomic_type instanceof Atomic\TLiteralString + if (!$atomic_type instanceof TLiteralString || InternalCallMapHandler::inCallMap($atomic_type->value) ) { continue; @@ -892,7 +904,7 @@ public static function verifyType( $potential_method_ids = []; foreach ($input_type->getAtomicTypes() as $input_type_part) { - if ($input_type_part instanceof Atomic\TKeyedArray) { + if ($input_type_part instanceof TKeyedArray) { $potential_method_id = CallableTypeComparator::getCallableMethodIdFromTKeyedArray( $input_type_part, $codebase, @@ -903,7 +915,7 @@ public static function verifyType( if ($potential_method_id && $potential_method_id !== 'not-callable') { $potential_method_ids[] = $potential_method_id; } - } elseif ($input_type_part instanceof Atomic\TLiteralString + } elseif ($input_type_part instanceof TLiteralString && strpos($input_type_part->value, '::') ) { $parts = explode('::', $input_type_part->value); @@ -1146,7 +1158,7 @@ public static function verifyType( */ private static function verifyExplicitParam( StatementsAnalyzer $statements_analyzer, - Type\Union $param_type, + Union $param_type, CodeLocation $arg_location, PhpParser\Node\Expr $input_expr, Context $context @@ -1154,7 +1166,7 @@ private static function verifyExplicitParam( $codebase = $statements_analyzer->getCodebase(); foreach ($param_type->getAtomicTypes() as $param_type_part) { - if ($param_type_part instanceof Atomic\TClassString + if ($param_type_part instanceof TClassString && $input_expr instanceof PhpParser\Node\Scalar\String_ && $param_type->isSingle() ) { @@ -1170,11 +1182,11 @@ private static function verifyExplicitParam( ) { return; } - } elseif ($param_type_part instanceof Atomic\TArray + } elseif ($param_type_part instanceof TArray && $input_expr instanceof PhpParser\Node\Expr\Array_ ) { foreach ($param_type_part->type_params[1]->getAtomicTypes() as $param_array_type_part) { - if ($param_array_type_part instanceof Atomic\TClassString) { + if ($param_array_type_part instanceof TClassString) { foreach ($input_expr->items as $item) { if ($item && $item->value instanceof PhpParser\Node\Scalar\String_) { if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName( @@ -1193,7 +1205,7 @@ private static function verifyExplicitParam( } } } - } elseif ($param_type_part instanceof Atomic\TCallable) { + } elseif ($param_type_part instanceof TCallable) { $can_be_callable_like_array = false; if ($param_type->hasArray()) { /** @@ -1202,11 +1214,11 @@ private static function verifyExplicitParam( $param_array_type = $param_type->getAtomicTypes()['array']; $row_type = null; - if ($param_array_type instanceof Atomic\TList) { + if ($param_array_type instanceof TList) { $row_type = $param_array_type->type_param; - } elseif ($param_array_type instanceof Atomic\TArray) { + } elseif ($param_array_type instanceof TArray) { $row_type = $param_array_type->type_params[1]; - } elseif ($param_array_type instanceof Atomic\TKeyedArray) { + } elseif ($param_array_type instanceof TKeyedArray) { $row_type = $param_array_type->getGenericArrayType()->type_params[1]; } @@ -1321,18 +1333,18 @@ private static function verifyExplicitParam( } /** - * @param Atomic\TKeyedArray|Atomic\TArray|Atomic\TList|Atomic\TClassStringMap $unpacked_atomic_array + * @param TKeyedArray|TArray|TList|TClassStringMap $unpacked_atomic_array */ private static function coerceValueAfterGatekeeperArgument( StatementsAnalyzer $statements_analyzer, - Type\Union $input_type, + Union $input_type, bool $input_type_changed, PhpParser\Node\Expr $input_expr, - Type\Union $param_type, - ?Type\Union $signature_param_type, + Union $param_type, + ?Union $signature_param_type, Context $context, bool $unpack, - ?Type\Atomic $unpacked_atomic_array + ?Atomic $unpacked_atomic_array ): void { if ($param_type->hasMixed()) { return; @@ -1342,9 +1354,9 @@ private static function coerceValueAfterGatekeeperArgument( $input_type = clone $input_type; foreach ($param_type->getAtomicTypes() as $param_atomic_type) { - if ($param_atomic_type instanceof Atomic\TGenericObject) { + if ($param_atomic_type instanceof TGenericObject) { foreach ($input_type->getAtomicTypes() as $input_atomic_type) { - if ($input_atomic_type instanceof Atomic\TGenericObject + if ($input_atomic_type instanceof TGenericObject && $input_atomic_type->value === $param_atomic_type->value ) { foreach ($input_atomic_type->type_params as $i => $type_param) { @@ -1415,26 +1427,26 @@ private static function coerceValueAfterGatekeeperArgument( } if ($unpack) { - if ($unpacked_atomic_array instanceof Atomic\TList) { + if ($unpacked_atomic_array instanceof TList) { $unpacked_atomic_array = clone $unpacked_atomic_array; $unpacked_atomic_array->type_param = $input_type; - $context->vars_in_scope[$var_id] = new Type\Union([$unpacked_atomic_array]); - } elseif ($unpacked_atomic_array instanceof Atomic\TArray) { + $context->vars_in_scope[$var_id] = new Union([$unpacked_atomic_array]); + } elseif ($unpacked_atomic_array instanceof TArray) { $unpacked_atomic_array = clone $unpacked_atomic_array; $unpacked_atomic_array->type_params[1] = $input_type; - $context->vars_in_scope[$var_id] = new Type\Union([$unpacked_atomic_array]); - } elseif ($unpacked_atomic_array instanceof Atomic\TKeyedArray + $context->vars_in_scope[$var_id] = new Union([$unpacked_atomic_array]); + } elseif ($unpacked_atomic_array instanceof TKeyedArray && $unpacked_atomic_array->is_list ) { $unpacked_atomic_array = $unpacked_atomic_array->getList(); $unpacked_atomic_array->type_param = $input_type; - $context->vars_in_scope[$var_id] = new Type\Union([$unpacked_atomic_array]); + $context->vars_in_scope[$var_id] = new Union([$unpacked_atomic_array]); } else { - $context->vars_in_scope[$var_id] = new Type\Union([ - new Atomic\TArray([ + $context->vars_in_scope[$var_id] = new Union([ + new TArray([ Type::getInt(), $input_type ]), @@ -1454,11 +1466,11 @@ private static function processTaintedness( CodeLocation $arg_location, CodeLocation $function_call_location, FunctionLikeParameter $function_param, - Type\Union $input_type, + Union $input_type, PhpParser\Node\Expr $expr, Context $context, bool $specialize_taint - ): Type\Union { + ): Union { $codebase = $statements_analyzer->getCodebase(); if (!$statements_analyzer->data_flow_graph diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php index 62960a608d6..9dd368ea64f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php @@ -1,4 +1,5 @@ inside_call = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $arg->value, $context) === false) { + $context->inside_call = $was_inside_call; + return false; } - if (!$was_inside_call) { - $context->inside_call = false; - } + $context->inside_call = $was_inside_call; if (($argument_offset === 0 && $method_id === 'array_filter' && count($args) === 2) || ($argument_offset > 0 && $method_id === 'array_map' && count($args) >= 2) @@ -243,11 +249,11 @@ private static function handleArrayMapFilterArrayArg( ): void { $codebase = $statements_analyzer->getCodebase(); - $generic_param_type = new Type\Union([ - new Type\Atomic\TArray([ + $generic_param_type = new Union([ + new TArray([ Type::getArrayKey(), - new Type\Union([ - new Type\Atomic\TTemplateParam( + new Union([ + new TTemplateParam( 'ArrayValue' . $argument_offset, Type::getMixed(), $method_id @@ -313,8 +319,8 @@ private static function handleClosureArg( $function_like_params[] = new FunctionLikeParameter( 'function', false, - new Type\Union([ - new Type\Atomic\TTemplateParam( + new Union([ + new TTemplateParam( $template_name, Type::getMixed(), $method_id @@ -323,8 +329,8 @@ private static function handleClosureArg( ); } - $replaced_type = new Type\Union([ - new Type\Atomic\TCallable( + $replaced_type = new Union([ + new TCallable( 'callable', array_reverse($function_like_params) ) @@ -396,8 +402,8 @@ function ($lower_bounds) use ($codebase) { if (!$has_different_docblock_type) { foreach ($replaced_type->getAtomicTypes() as $replaced_type_part) { - if ($replaced_type_part instanceof Type\Atomic\TCallable - || $replaced_type_part instanceof Type\Atomic\TClosure + if ($replaced_type_part instanceof TCallable + || $replaced_type_part instanceof TClosure ) { if (isset($replaced_type_part->params[$closure_param_offset]->type) && !$replaced_type_part->params[$closure_param_offset]->type->hasTemplate() @@ -603,7 +609,7 @@ public static function checkArgumentsMatch( && $function_params[$i]->type && $function_params[$i]->type->hasTemplate() ) { - if ($function_params[$i]->default_type instanceof Type\Union) { + if ($function_params[$i]->default_type instanceof Union) { $default_type = $function_params[$i]->default_type; } else { $default_type_atomic = ConstantTypeResolver::resolve( @@ -612,7 +618,7 @@ public static function checkArgumentsMatch( $statements_analyzer ); - $default_type = new Type\Union([$default_type_atomic]); + $default_type = new Union([$default_type_atomic]); } if ($default_type->hasLiteralValue()) { @@ -681,7 +687,7 @@ public static function checkArgumentsMatch( $key_types = $array_type->getGenericArrayType()->getChildNodes()[0]->getChildNodes(); foreach ($key_types as $key_type) { - if (!$key_type instanceof Type\Atomic\TLiteralString + if (!$key_type instanceof TLiteralString || ($function_storage && !$function_storage->allow_named_arg_calls)) { continue; } @@ -1053,8 +1059,8 @@ private static function handlePossiblyMatchingByRefParam( } if ($by_ref_type && $function_param->is_variadic && $arg->unpack) { - $by_ref_type = new Type\Union([ - new Type\Atomic\TArray([ + $by_ref_type = new Union([ + new TArray([ Type::getInt(), $by_ref_type, ]), @@ -1112,12 +1118,12 @@ private static function evaluateArbitraryParam( $context->inside_call = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $arg->value, $context) === false) { + $context->inside_call = $was_inside_call; + return false; } - if (!$was_inside_call) { - $context->inside_call = false; - } + $context->inside_call = $was_inside_call; } if ($arg->value instanceof PhpParser\Node\Expr\PropertyFetch @@ -1223,6 +1229,8 @@ private static function handleByRefFunctionArg( $arg->value, $context ) === false) { + $context->inside_assignment = $was_inside_assignment; + return false; } @@ -1271,7 +1279,7 @@ private static function handleByRefFunctionArg( $array_type = new TArray([Type::getInt(), $array_type->type_param]); } - $by_ref_type = new Type\Union([clone $array_type]); + $by_ref_type = new Union([clone $array_type]); AssignmentAnalyzer::assignByRefParam( $statements_analyzer, @@ -1318,7 +1326,7 @@ private static function handleByRefFunctionArg( /** * @param list $args * @param array $function_params - * @param array> $class_generic_params + * @param array> $class_generic_params */ private static function getProvisionalTemplateResultForFunctionLike( StatementsAnalyzer $statements_analyzer, @@ -1501,7 +1509,7 @@ private static function checkArgCount( && !$param->is_variadic && $template_result ) { - if ($param->default_type instanceof Type\Union) { + if ($param->default_type instanceof Union) { $default_type = clone $param->default_type; } else { $default_type_atomic = ConstantTypeResolver::resolve( @@ -1510,7 +1518,7 @@ private static function checkArgCount( $statements_analyzer ); - $default_type = new Type\Union([$default_type_atomic]); + $default_type = new Union([$default_type_atomic]); } TemplateStandinTypeReplacer::replace( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php index 1e39312f457..654e9bd829b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php @@ -1,4 +1,5 @@ value, $context ) === false) { + $context->inside_assignment = $was_inside_assignment; + return false; } @@ -232,7 +239,7 @@ function ($arg) { } } - $by_ref_type = new Type\Union([clone $array_type]); + $by_ref_type = new Union([clone $array_type]); foreach ($args as $argument_offset => $arg) { if ($argument_offset === 0) { @@ -258,7 +265,7 @@ function ($arg) { ) { $by_ref_type = Type::combineUnionTypes( $by_ref_type, - new Type\Union([new TArray([$new_offset_type, Type::getMixed()])]) + new Union([new TArray([$new_offset_type, Type::getMixed()])]) ); } elseif ($arg->unpack) { $arg_value_type = clone $arg_value_type; @@ -289,11 +296,11 @@ function ($arg) { if ($objectlike_list) { array_unshift($objectlike_list->properties, $arg_value_type); - $by_ref_type = new Type\Union([$objectlike_list]); + $by_ref_type = new Union([$objectlike_list]); } elseif ($array_type instanceof TList) { $by_ref_type = Type::combineUnionTypes( $by_ref_type, - new Type\Union( + new Union( [ new TNonEmptyList(clone $arg_value_type), ] @@ -302,7 +309,7 @@ function ($arg) { } else { $by_ref_type = Type::combineUnionTypes( $by_ref_type, - new Type\Union( + new Union( [ new TNonEmptyArray( [ @@ -402,8 +409,8 @@ public static function handleSplice( && $replacement_arg_type->hasString() && $replacement_arg_type->isSingle() ) { - $replacement_arg_type = new Type\Union([ - new Type\Atomic\TArray([Type::getInt(), $replacement_arg_type]) + $replacement_arg_type = new Union([ + new TArray([Type::getInt(), $replacement_arg_type]) ]); $statements_analyzer->node_data->setType($replacement_arg, $replacement_arg_type); @@ -517,7 +524,7 @@ public static function handleByRefArrayAdjustment( array_shift($array_properties); if (!$array_properties) { - $array_atomic_type = new Type\Atomic\TList( + $array_atomic_type = new TList( $array_atomic_type->previous_value_type ?: Type::getMixed() ); @@ -537,8 +544,8 @@ public static function handleByRefArrayAdjustment( if ($array_atomic_type->count === 0) { $array_atomic_type = new TArray( [ - new Type\Union([new TEmpty]), - new Type\Union([new TEmpty]), + new Union([new TEmpty]), + new Union([new TEmpty]), ] ); } else { @@ -554,8 +561,8 @@ public static function handleByRefArrayAdjustment( if ($array_atomic_type->count === 0) { $array_atomic_type = new TArray( [ - new Type\Union([new TEmpty]), - new Type\Union([new TEmpty]), + new Union([new TEmpty]), + new Union([new TEmpty]), ] ); } else { @@ -583,7 +590,7 @@ private static function checkClosureType( StatementsAnalyzer $statements_analyzer, Context $context, string $method_id, - Type\Atomic $closure_type, + Atomic $closure_type, PhpParser\Node\Arg $closure_arg, int $min_closure_param_count, int $max_closure_param_count, @@ -592,7 +599,7 @@ private static function checkClosureType( ): void { $codebase = $statements_analyzer->getCodebase(); - if (!$closure_type instanceof Type\Atomic\TClosure) { + if (!$closure_type instanceof TClosure) { if ($method_id === 'array_map') { return; } @@ -657,7 +664,7 @@ private static function checkClosureType( continue; } - $closure_types[] = new Type\Atomic\TClosure( + $closure_types[] = new TClosure( 'Closure', $method_storage->params, $method_storage->return_type ?: Type::getMixed() @@ -710,7 +717,7 @@ private static function checkClosureType( $closure_types[] = $callmap_callables[0]; } } else { - $closure_types[] = new Type\Atomic\TClosure( + $closure_types[] = new TClosure( 'Closure', $function_storage->params, $function_storage->return_type ?: Type::getMixed() @@ -741,14 +748,14 @@ private static function checkClosureType( } /** - * @param Type\Atomic\TClosure|Type\Atomic\TCallable $closure_type + * @param TClosure|TCallable $closure_type * @param (TArray|null)[] $array_arg_types */ private static function checkClosureTypeArgs( StatementsAnalyzer $statements_analyzer, Context $context, string $method_id, - Type\Atomic $closure_type, + Atomic $closure_type, PhpParser\Node\Arg $closure_arg, int $min_closure_param_count, int $max_closure_param_count, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php index 0c5bd1475f9..1450e9e30af 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php @@ -1,4 +1,5 @@ >|null + * @return array>|null * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ @@ -27,7 +29,7 @@ public static function collect( ClassLikeStorage $class_storage, ClassLikeStorage $static_class_storage, ?string $method_name = null, - ?Type\Atomic $lhs_type_part = null, + ?Atomic $lhs_type_part = null, bool $self_call = false ): ?array { $static_fq_class_name = $static_class_storage->name; @@ -140,7 +142,7 @@ public static function collect( && isset($e[$candidate_class_storage->name][$type_name]) && !isset($class_template_params[$type_name][$candidate_class_storage->name]) ) { - $class_template_params[$type_name][$candidate_class_storage->name] = new Type\Union( + $class_template_params[$type_name][$candidate_class_storage->name] = new Union( self::expandType( $codebase, $e[$candidate_class_storage->name][$type_name], @@ -170,7 +172,7 @@ public static function resolveTemplateParam( ): ?Union { $output_type_extends = null; foreach ($input_type_extends->getAtomicTypes() as $type_extends_atomic) { - if ($type_extends_atomic instanceof Type\Atomic\TTemplateParam) { + if ($type_extends_atomic instanceof TTemplateParam) { if (isset( $static_class_storage ->template_types @@ -215,7 +217,7 @@ public static function resolveTemplateParam( } } else { $output_type_extends = Type::combineUnionTypes( - new Type\Union([$type_extends_atomic]), + new Union([$type_extends_atomic]), $output_type_extends ); } @@ -224,12 +226,12 @@ public static function resolveTemplateParam( } /** - * @param array> $e - * @return non-empty-list + * @param array> $e + * @return non-empty-list */ private static function expandType( Codebase $codebase, - Type\Union $input_type_extends, + Union $input_type_extends, array $e, string $static_fq_class_name, ?array $static_template_types @@ -237,7 +239,7 @@ private static function expandType( $output_type_extends = []; foreach ($input_type_extends->getAtomicTypes() as $type_extends_atomic) { - if ($type_extends_atomic instanceof Type\Atomic\TTemplateParam + if ($type_extends_atomic instanceof TTemplateParam && ($static_fq_class_name !== $type_extends_atomic->defining_class || !isset($static_template_types[$type_extends_atomic->param_name])) && isset($e[$type_extends_atomic->defining_class][$type_extends_atomic->param_name]) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php index 6ff81fb906c..d59208dfd36 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php @@ -1,4 +1,5 @@ params, $candidate_callable->return_type, @@ -627,7 +635,7 @@ private static function getAnalyzeNamedExpression( continue; } - if ($var_type_part instanceof Type\Atomic\TClosure || $var_type_part instanceof TCallable) { + if ($var_type_part instanceof TClosure || $var_type_part instanceof TCallable) { if (!$var_type_part->is_pure && $context->pure) { IssueBuffer::maybeAdd( new ImpureFunctionCall( @@ -657,7 +665,7 @@ private static function getAnalyzeNamedExpression( ); } - if ($var_type_part instanceof Type\Atomic\TClosure) { + if ($var_type_part instanceof TClosure) { $function_call_info->byref_uses += $var_type_part->byref_uses; } @@ -682,14 +690,14 @@ private static function getAnalyzeNamedExpression( // this is fine $has_valid_function_call_type = true; } elseif ($var_type_part instanceof TString - || $var_type_part instanceof Type\Atomic\TArray - || $var_type_part instanceof Type\Atomic\TList - || ($var_type_part instanceof Type\Atomic\TKeyedArray + || $var_type_part instanceof TArray + || $var_type_part instanceof TList + || ($var_type_part instanceof TKeyedArray && count($var_type_part->properties) === 2) ) { $potential_method_id = null; - if ($var_type_part instanceof Type\Atomic\TKeyedArray) { + if ($var_type_part instanceof TKeyedArray) { $potential_method_id = CallableTypeComparator::getCallableMethodIdFromTKeyedArray( $var_type_part, $codebase, @@ -700,7 +708,7 @@ private static function getAnalyzeNamedExpression( if ($potential_method_id === 'not-callable') { $potential_method_id = null; } - } elseif ($var_type_part instanceof Type\Atomic\TLiteralString) { + } elseif ($var_type_part instanceof TLiteralString) { if (!$var_type_part->value) { $invalid_function_call_types[] = '\'\''; continue; @@ -828,7 +836,7 @@ private static function analyzeInvokeCall( PhpParser\Node\Expr\FuncCall $real_stmt, PhpParser\Node\Expr $function_name, Context $context, - Type\Atomic $atomic_type + Atomic $atomic_type ): void { $old_data_provider = $statements_analyzer->node_data; @@ -846,7 +854,7 @@ private static function analyzeInvokeCall( $statements_analyzer->addSuppressedIssues(['InternalMethod']); } - $statements_analyzer->node_data->setType($function_name, new Type\Union([$atomic_type])); + $statements_analyzer->node_data->setType($function_name, new Union([$atomic_type])); MethodCallAnalyzer::analyze( $statements_analyzer, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallInfo.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallInfo.php index d3e963384aa..61984a89d7d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallInfo.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallInfo.php @@ -1,10 +1,11 @@ + * @var array */ public $defined_constants = []; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php index e0134327cb7..7a61039cdc1 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php @@ -1,4 +1,5 @@ config; if ($stmt->isFirstClassCallable()) { $candidate_callable = CallableTypeComparator::getCallableFromAtomic( $codebase, - new Type\Atomic\TLiteralString($function_id), + new TLiteralString($function_id), null, $statements_analyzer, true ); if ($candidate_callable) { - $stmt_type = new Type\Union([new Type\Atomic\TClosure( + $stmt_type = new Union([new TClosure( 'Closure', $candidate_callable->params, $candidate_callable->return_type, @@ -298,7 +318,7 @@ private static function getReturnTypeFromCallMapWithArgs( array $call_args, TCallable $callmap_callable, Context $context - ): Type\Union { + ): Union { $call_map_key = strtolower($function_id); $codebase = $statements_analyzer->getCodebase(); @@ -306,18 +326,18 @@ private static function getReturnTypeFromCallMapWithArgs( if (!$call_args) { switch ($call_map_key) { case 'hrtime': - return new Type\Union([ - new Type\Atomic\TKeyedArray([ + return new Union([ + new TKeyedArray([ Type::getInt(), Type::getInt() ]) ]); case 'get_called_class': - return new Type\Union([ - new Type\Atomic\TClassString( + return new Union([ + new TClassString( $context->self ?: 'object', - $context->self ? new Type\Atomic\TNamedObject($context->self, true) : null + $context->self ? new TNamedObject($context->self, true) : null ) ]); @@ -326,8 +346,8 @@ private static function getReturnTypeFromCallMapWithArgs( $classlike_storage = $codebase->classlike_storage_provider->get($context->self); if ($classlike_storage->parent_classes) { - return new Type\Union([ - new Type\Atomic\TClassString( + return new Union([ + new TClassString( array_values($classlike_storage->parent_classes)[0] ) ]); @@ -342,30 +362,30 @@ private static function getReturnTypeFromCallMapWithArgs( if (count($atomic_types) === 1) { if (isset($atomic_types['array'])) { - if ($atomic_types['array'] instanceof Type\Atomic\TCallableArray - || $atomic_types['array'] instanceof Type\Atomic\TCallableList - || $atomic_types['array'] instanceof Type\Atomic\TCallableKeyedArray + if ($atomic_types['array'] instanceof TCallableArray + || $atomic_types['array'] instanceof TCallableList + || $atomic_types['array'] instanceof TCallableKeyedArray ) { return Type::getInt(false, 2); } - if ($atomic_types['array'] instanceof Type\Atomic\TNonEmptyArray) { - return new Type\Union([ + if ($atomic_types['array'] instanceof TNonEmptyArray) { + return new Union([ $atomic_types['array']->count !== null - ? new Type\Atomic\TLiteralInt($atomic_types['array']->count) - : new Type\Atomic\TPositiveInt + ? new TLiteralInt($atomic_types['array']->count) + : new TPositiveInt ]); } - if ($atomic_types['array'] instanceof Type\Atomic\TNonEmptyList) { - return new Type\Union([ + if ($atomic_types['array'] instanceof TNonEmptyList) { + return new Union([ $atomic_types['array']->count !== null - ? new Type\Atomic\TLiteralInt($atomic_types['array']->count) - : new Type\Atomic\TPositiveInt + ? new TLiteralInt($atomic_types['array']->count) + : new TPositiveInt ]); } - if ($atomic_types['array'] instanceof Type\Atomic\TKeyedArray) { + if ($atomic_types['array'] instanceof TKeyedArray) { $min = 0; $max = 0; foreach ($atomic_types['array']->properties as $property) { @@ -386,26 +406,26 @@ private static function getReturnTypeFromCallMapWithArgs( if ($atomic_types['array']->sealed) { //the KeyedArray is sealed, we can use the min and max if ($min === $max) { - return new Type\Union([new Type\Atomic\TLiteralInt($max)]); + return new Union([new TLiteralInt($max)]); } - return new Type\Union([new Type\Atomic\TIntRange($min, $max)]); + return new Union([new TIntRange($min, $max)]); } //the type is not sealed, we can only use the min - return new Type\Union([new Type\Atomic\TIntRange($min, null)]); + return new Union([new TIntRange($min, null)]); } - if ($atomic_types['array'] instanceof Type\Atomic\TArray + if ($atomic_types['array'] instanceof TArray && $atomic_types['array']->type_params[0]->isEmpty() && $atomic_types['array']->type_params[1]->isEmpty() ) { return Type::getInt(false, 0); } - return new Type\Union([ - new Type\Atomic\TLiteralInt(0), - new Type\Atomic\TPositiveInt + return new Union([ + new TLiteralInt(0), + new TPositiveInt ]); } } @@ -422,20 +442,20 @@ private static function getReturnTypeFromCallMapWithArgs( } if ((string) $first_arg_type === 'false') { - return new Type\Union([ - new Type\Atomic\TKeyedArray([ + return new Union([ + new TKeyedArray([ Type::getInt(), Type::getInt() ]) ]); } - return new Type\Union([ - new Type\Atomic\TKeyedArray([ + return new Union([ + new TKeyedArray([ Type::getInt(), Type::getInt() ]), - new Type\Atomic\TInt() + new TInt() ]); } @@ -452,15 +472,15 @@ private static function getReturnTypeFromCallMapWithArgs( if ($first_arg_type->hasArray()) { /** @psalm-suppress PossiblyUndefinedStringArrayOffset */ $array_type = $first_arg_type->getAtomicTypes()['array']; - if ($array_type instanceof Type\Atomic\TKeyedArray) { + if ($array_type instanceof TKeyedArray) { return $array_type->getGenericValueType(); } - if ($array_type instanceof Type\Atomic\TArray) { + if ($array_type instanceof TArray) { return clone $array_type->type_params[1]; } - if ($array_type instanceof Type\Atomic\TList) { + if ($array_type instanceof TList) { return clone $array_type->type_param; } } elseif ($first_arg_type->hasScalarType() @@ -487,15 +507,15 @@ private static function getReturnTypeFromCallMapWithArgs( case 'fgetcsv': $string_type = Type::getString(); - $string_type->addType(new Type\Atomic\TNull); + $string_type->addType(new TNull); $string_type->ignore_nullable_issues = true; - $call_map_return_type = new Type\Union([ - new Type\Atomic\TNonEmptyList( + $call_map_return_type = new Union([ + new TNonEmptyList( $string_type ), - new Type\Atomic\TFalse, - new Type\Atomic\TNull + new TFalse, + new TNull ]); if ($codebase->config->ignore_internal_nullable_issues) { @@ -556,7 +576,7 @@ private static function taintReturnType( PhpParser\Node\Expr\FuncCall $stmt, string $function_id, FunctionLikeStorage $function_storage, - Type\Union $stmt_type, + Union $stmt_type, TemplateResult $template_result, Context $context ): ?DataFlowNode { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php index 7d6ee6d94ad..fe0c730e402 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php @@ -1,4 +1,5 @@ extra_types = $extra_types; - } elseif ($lhs_type_part instanceof Type\Atomic\TObject && $extra_types) { + } elseif ($lhs_type_part instanceof TObject && $extra_types) { $lhs_type_part = array_shift($extra_types); if ($extra_types) { $lhs_type_part->extra_types = $extra_types; @@ -235,7 +247,7 @@ public static function analyze( // @mixin attributes are an absolute pain! Lots of complexity here, // as they can redefine the called class, method id etc. if ($class_storage->templatedMixins - && $lhs_type_part instanceof Type\Atomic\TGenericObject + && $lhs_type_part instanceof TGenericObject && $class_storage->template_types ) { [$lhs_type_part, $class_storage, $naive_method_exists, $method_id, $fq_class_name] @@ -457,17 +469,17 @@ public static function analyze( /** * @param TNamedObject|TTemplateParam $lhs_type_part - * @param array $intersection_types + * @param array $intersection_types * - * @return array{?Type\Union, array} + * @return array{?Union, array} */ private static function getIntersectionReturnType( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\MethodCall $stmt, Codebase $codebase, Context $context, - Type\Union $lhs_type, - Type\Atomic $lhs_type_part, + Union $lhs_type, + Atomic $lhs_type_part, ?string $lhs_var_id, AtomicMethodCallAnalysisResult $result, array $intersection_types @@ -478,7 +490,7 @@ private static function getIntersectionReturnType( foreach ($intersection_types as $intersection_type) { $intersection_result = clone $result; - /** @var ?Type\Union $intersection_result->return_type */ + /** @var ?Union $intersection_result->return_type */ $intersection_result->return_type = null; self::analyze( @@ -525,8 +537,8 @@ private static function getIntersectionReturnType( private static function updateResultReturnType( AtomicMethodCallAnalysisResult $result, - Type\Union $return_type_candidate, - ?Type\Union $all_intersection_return_type, + Union $return_type_candidate, + ?Union $all_intersection_return_type, Codebase $codebase ): void { if ($all_intersection_return_type) { @@ -544,26 +556,26 @@ private static function handleInvalidClass( StatementsAnalyzer $statements_analyzer, Codebase $codebase, PhpParser\Node\Expr\MethodCall $stmt, - Type\Union $lhs_type, - Type\Atomic $lhs_type_part, + Union $lhs_type, + Atomic $lhs_type_part, ?string $lhs_var_id, Context $context, bool $is_intersection, AtomicMethodCallAnalysisResult $result ): void { switch (get_class($lhs_type_part)) { - case Type\Atomic\TNull::class: - case Type\Atomic\TFalse::class: + case TNull::class: + case TFalse::class: // handled above return; case TTemplateParam::class: - case Type\Atomic\TEmptyMixed::class: - case Type\Atomic\TEmpty::class: - case Type\Atomic\TMixed::class: - case Type\Atomic\TNonEmptyMixed::class: - case Type\Atomic\TObject::class: - case Type\Atomic\TObjectWithProperties::class: + case TEmptyMixed::class: + case TEmpty::class: + case TMixed::class: + case TNonEmptyMixed::class: + case TObject::class: + case TObjectWithProperties::class: if (!$context->collect_initializations && !$context->collect_mutations && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath() @@ -576,7 +588,7 @@ private static function handleInvalidClass( $result->has_mixed_method_call = true; - if ($lhs_type_part instanceof Type\Atomic\TObjectWithProperties + if ($lhs_type_part instanceof TObjectWithProperties && $stmt->name instanceof PhpParser\Node\Identifier && isset($lhs_type_part->methods[$stmt->name->name]) ) { @@ -670,7 +682,7 @@ private static function handleTemplatedMixins( $naive_method_exists = false; if ($class_storage->templatedMixins - && $lhs_type_part instanceof Type\Atomic\TGenericObject + && $lhs_type_part instanceof TGenericObject && $class_storage->template_types ) { $template_type_keys = array_keys($class_storage->template_types); @@ -804,7 +816,7 @@ private static function handleRegularMixins( $lhs_type_expanded = TypeExpander::expandUnion( $codebase, - new Type\Union([$lhs_type_part]), + new Union([$lhs_type_part]), $mixin_declaring_class_storage->name, $fq_class_name, $class_storage->parent_class, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php index 4110e8c48e2..2dc0c0592d5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php @@ -1,4 +1,5 @@ $args */ public static function analyze( @@ -53,12 +58,12 @@ public static function analyze( array $args, Codebase $codebase, Context $context, - Type\Atomic\TNamedObject $lhs_type_part, - ?Type\Atomic $static_type, + TNamedObject $lhs_type_part, + ?Atomic $static_type, ?string $lhs_var_id, MethodIdentifier $method_id, AtomicMethodCallAnalysisResult $result - ): Type\Union { + ): Union { $config = $codebase->config; $fq_class_name = $lhs_type_part->value; @@ -277,7 +282,7 @@ public static function analyze( $class_storage->parent_class, true, false, - $static_type instanceof Type\Atomic\TNamedObject + $static_type instanceof TNamedObject && $codebase->classlike_storage_provider->get($static_type->value)->final, true ); @@ -299,7 +304,7 @@ public static function analyze( $class_storage->parent_class, true, false, - $static_type instanceof Type\Atomic\TNamedObject + $static_type instanceof TNamedObject && $codebase->classlike_storage_provider->get($static_type->value)->final, true ); @@ -474,7 +479,7 @@ private static function getMagicGetterOrSetterProperty( PhpParser\Node\Identifier $stmt_name, Context $context, string $fq_class_name - ): ?Type\Union { + ): ?Union { $method_name = strtolower($stmt_name->name); if (!in_array($method_name, ['__get', '__set'], true)) { return null; @@ -530,7 +535,7 @@ private static function getMagicGetterOrSetterProperty( $codebase, $class_storage->pseudo_property_set_types['$' . $prop_name], $fq_class_name, - new Type\Atomic\TNamedObject($fq_class_name), + new TNamedObject($fq_class_name), $class_storage->parent_class ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php index c78ec1af3d2..a3f4660a6ae 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php @@ -1,4 +1,5 @@ $args */ public static function fetch( @@ -43,12 +49,12 @@ public static function fetch( ?MethodIdentifier $declaring_method_id, MethodIdentifier $premixin_method_id, string $cased_method_id, - Type\Atomic $lhs_type_part, - ?Type\Atomic $static_type, + Atomic $lhs_type_part, + ?Atomic $static_type, array $args, AtomicMethodCallAnalysisResult $result, TemplateResult $template_result - ): Type\Union { + ): Union { $call_map_id = $declaring_method_id ?? $method_id; $fq_class_name = $method_id->fq_class_name; @@ -141,7 +147,7 @@ public static function fetch( $method_storage = ($class_storage->methods[$method_id->method_name] ?? null); if ($method_storage) { - $return_type_candidate = new Type\Union([new Type\Atomic\TClosure( + $return_type_candidate = new Union([new TClosure( 'Closure', $method_storage->params, $method_storage->return_type, @@ -170,7 +176,7 @@ public static function fetch( $class_storage->parent_class, true, false, - $static_type instanceof Type\Atomic\TNamedObject + $static_type instanceof TNamedObject && $codebase->classlike_storage_provider->get($static_type->value)->final, true ); @@ -192,7 +198,7 @@ public static function fetch( $class_storage->parent_class, true, false, - $static_type instanceof Type\Atomic\TNamedObject + $static_type instanceof TNamedObject && $codebase->classlike_storage_provider->get($static_type->value)->final, true ); @@ -253,7 +259,7 @@ public static function fetch( */ public static function taintMethodCallResult( StatementsAnalyzer $statements_analyzer, - Type\Union $return_type_candidate, + Union $return_type_candidate, PhpParser\Node $name_expr, PhpParser\Node\Expr $var_expr, array $args, @@ -536,12 +542,12 @@ function ($parent_node) { } public static function replaceTemplateTypes( - Type\Union $return_type_candidate, + Union $return_type_candidate, TemplateResult $template_result, MethodIdentifier $method_id, int $arg_count, Codebase $codebase - ): Type\Union { + ): Union { if ($template_result->template_types) { $bindable_template_types = $return_type_candidate->getTemplateTypes(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php index ddfa8fc315b..48ff4dac803 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php @@ -1,4 +1,5 @@ fq_class_name; @@ -199,7 +201,7 @@ public static function handleMissingOrMagicMethod( bool $is_interface, Context $context, Config $config, - ?Type\Union $all_intersection_return_type, + ?Union $all_intersection_return_type, array $all_intersection_existent_method_ids, ?string $intersection_method_id, string $cased_method_id, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php index ff15829d5d1..eef4a321e24 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php @@ -1,9 +1,12 @@ node_data->setType($stmt->var, $existing_stmt_var_type); } elseif (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context) === false) { + $context->inside_call = $was_inside_call; + return false; } @@ -65,6 +72,8 @@ public static function analyze( $context->inside_call = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->name, $context) === false) { + $context->inside_call = $was_inside_call; + return false; } } @@ -173,19 +182,19 @@ public static function analyze( $lhs_types = $class_type->getAtomicTypes(); - $result = new Method\AtomicMethodCallAnalysisResult(); + $result = new AtomicMethodCallAnalysisResult(); $possible_new_class_types = []; foreach ($lhs_types as $lhs_type_part) { - Method\AtomicMethodCallAnalyzer::analyze( + AtomicMethodCallAnalyzer::analyze( $statements_analyzer, $stmt, $codebase, $context, $class_type, $lhs_type_part, - $lhs_type_part instanceof Type\Atomic\TNamedObject - || $lhs_type_part instanceof Type\Atomic\TTemplateParam + $lhs_type_part instanceof TNamedObject + || $lhs_type_part instanceof TTemplateParam ? $lhs_type_part : null, false, @@ -193,7 +202,7 @@ public static function analyze( $result ); if (isset($context->vars_in_scope[$lhs_var_id]) - && ($possible_new_class_type = $context->vars_in_scope[$lhs_var_id]) instanceof Type\Union + && ($possible_new_class_type = $context->vars_in_scope[$lhs_var_id]) instanceof Union && !$possible_new_class_type->equals($class_type)) { $possible_new_class_types[] = $context->vars_in_scope[$lhs_var_id]; } @@ -221,7 +230,7 @@ public static function analyze( if (count($possible_new_class_types) > 0) { $class_type = array_reduce( $possible_new_class_types, - function (?Type\Union $type_1, Type\Union $type_2) use ($codebase): Type\Union { + function (?Union $type_1, Union $type_2) use ($codebase): Union { return Type::combineUnionTypes($type_1, $type_2, $codebase); } ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php index 2c273a0782c..3fb74057c14 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php @@ -1,4 +1,5 @@ from_docblock) { @@ -478,19 +495,19 @@ private static function handleDependentTypeFunction( if (isset($context->vars_in_scope[$var_id])) { if (!$context->vars_in_scope[$var_id]->hasTemplate()) { if ($function_id === 'get_class') { - $atomic_type = new Type\Atomic\TDependentGetClass( + $atomic_type = new TDependentGetClass( $var_id, $context->vars_in_scope[$var_id]->hasMixed() ? Type::getObject() : $context->vars_in_scope[$var_id] ); } elseif ($function_id === 'gettype') { - $atomic_type = new Type\Atomic\TDependentGetType($var_id); + $atomic_type = new TDependentGetType($var_id); } else { - $atomic_type = new Type\Atomic\TDependentGetDebugType($var_id); + $atomic_type = new TDependentGetDebugType($var_id); } - $statements_analyzer->node_data->setType($real_stmt, new Type\Union([$atomic_type])); + $statements_analyzer->node_data->setType($real_stmt, new Union([$atomic_type])); return; } @@ -505,22 +522,22 @@ private static function handleDependentTypeFunction( $class_string_types = []; foreach ($var_type->getAtomicTypes() as $class_type) { - if ($class_type instanceof Type\Atomic\TNamedObject) { - $class_string_types[] = new Type\Atomic\TClassString($class_type->value, clone $class_type); - } elseif ($class_type instanceof Type\Atomic\TTemplateParam + if ($class_type instanceof TNamedObject) { + $class_string_types[] = new TClassString($class_type->value, clone $class_type); + } elseif ($class_type instanceof TTemplateParam && $class_type->as->isSingle() ) { $as_atomic_type = $class_type->as->getSingleAtomic(); - if ($as_atomic_type instanceof Type\Atomic\TObject) { - $class_string_types[] = new Type\Atomic\TTemplateParamClass( + if ($as_atomic_type instanceof TObject) { + $class_string_types[] = new TTemplateParamClass( $class_type->param_name, 'object', null, $class_type->defining_class ); } elseif ($as_atomic_type instanceof TNamedObject) { - $class_string_types[] = new Type\Atomic\TTemplateParamClass( + $class_string_types[] = new TTemplateParamClass( $class_type->param_name, $as_atomic_type->value, $as_atomic_type, @@ -528,28 +545,28 @@ private static function handleDependentTypeFunction( ); } } elseif ($function_id === 'get_class') { - $class_string_types[] = new Type\Atomic\TClassString(); + $class_string_types[] = new TClassString(); } else { - if ($class_type instanceof Type\Atomic\TInt) { - $class_string_types[] = new Type\Atomic\TLiteralString('int'); - } elseif ($class_type instanceof Type\Atomic\TString) { - $class_string_types[] = new Type\Atomic\TLiteralString('string'); - } elseif ($class_type instanceof Type\Atomic\TFloat) { - $class_string_types[] = new Type\Atomic\TLiteralString('float'); - } elseif ($class_type instanceof Type\Atomic\TBool) { - $class_string_types[] = new Type\Atomic\TLiteralString('bool'); - } elseif ($class_type instanceof Type\Atomic\TClosedResource) { - $class_string_types[] = new Type\Atomic\TLiteralString('resource (closed)'); - } elseif ($class_type instanceof Type\Atomic\TNull) { - $class_string_types[] = new Type\Atomic\TLiteralString('null'); + if ($class_type instanceof TInt) { + $class_string_types[] = new TLiteralString('int'); + } elseif ($class_type instanceof TString) { + $class_string_types[] = new TLiteralString('string'); + } elseif ($class_type instanceof TFloat) { + $class_string_types[] = new TLiteralString('float'); + } elseif ($class_type instanceof TBool) { + $class_string_types[] = new TLiteralString('bool'); + } elseif ($class_type instanceof TClosedResource) { + $class_string_types[] = new TLiteralString('resource (closed)'); + } elseif ($class_type instanceof TNull) { + $class_string_types[] = new TLiteralString('null'); } else { - $class_string_types[] = new Type\Atomic\TString(); + $class_string_types[] = new TString(); } } } if ($class_string_types) { - $statements_analyzer->node_data->setType($real_stmt, new Type\Union($class_string_types)); + $statements_analyzer->node_data->setType($real_stmt, new Union($class_string_types)); } } } elseif ($function_id === 'get_class' @@ -557,10 +574,10 @@ private static function handleDependentTypeFunction( ) { $statements_analyzer->node_data->setType( $real_stmt, - new Type\Union([ - new Type\Atomic\TClassString( + new Union([ + new TClassString( $get_class_name, - new Type\Atomic\TNamedObject($get_class_name) + new TNamedObject($get_class_name) ) ]) ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index b274be2ed2e..2f9548c4195 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -1,4 +1,5 @@ class instanceof PhpParser\Node\Stmt\Class_) { $extends = $stmt->class->extends ? (string) $stmt->class->extends : null; - $result_atomic_type = new Type\Atomic\TAnonymousClassInstance($fq_class_name, false, $extends); + $result_atomic_type = new TAnonymousClassInstance($fq_class_name, false, $extends); } else { //if the class is a Name, it can't represent a child $definite_class = $stmt->class instanceof PhpParser\Node\Name; @@ -215,7 +231,7 @@ public static function analyze( $statements_analyzer->node_data->setType( $stmt, - new Type\Union([$result_atomic_type]) + new Union([$result_atomic_type]) ); if (strtolower($fq_class_name) !== 'stdclass' && @@ -380,7 +396,7 @@ private static function analyzeNamedConstructor( return; } - if (Method\MethodVisibilityAnalyzer::analyze( + if (MethodVisibilityAnalyzer::analyze( $method_id, $context, $statements_analyzer->getSource(), @@ -513,7 +529,7 @@ function ($bounds) use ($codebase) { } if ($generic_param_types) { - $result_atomic_type = new Type\Atomic\TGenericObject( + $result_atomic_type = new TGenericObject( $fq_class_name, $generic_param_types ); @@ -522,7 +538,7 @@ function ($bounds) use ($codebase) { $statements_analyzer->node_data->setType( $stmt, - new Type\Union([$result_atomic_type]) + new Union([$result_atomic_type]) ); } } elseif ($stmt->getArgs()) { @@ -535,7 +551,7 @@ function ($bounds) use ($codebase) { $statements_analyzer->getSuppressedIssues() ); } elseif ($storage->template_types) { - $result_atomic_type = new Type\Atomic\TGenericObject( + $result_atomic_type = new TGenericObject( $fq_class_name, array_values( array_map( @@ -551,7 +567,7 @@ function ($map) { $statements_analyzer->node_data->setType( $stmt, - new Type\Union([$result_atomic_type]) + new Union([$result_atomic_type]) ); } @@ -655,17 +671,17 @@ private static function analyzeConstructorExpression( while ($stmt_class_types) { $lhs_type_part = array_shift($stmt_class_types); - if ($lhs_type_part instanceof Type\Atomic\TTemplateParam) { + if ($lhs_type_part instanceof TTemplateParam) { $stmt_class_types = array_merge($stmt_class_types, $lhs_type_part->as->getAtomicTypes()); continue; } - if ($lhs_type_part instanceof Type\Atomic\TTemplateParamClass) { + if ($lhs_type_part instanceof TTemplateParamClass) { if (!$statements_analyzer->node_data->getType($stmt)) { - $new_type_part = new Type\Atomic\TTemplateParam( + $new_type_part = new TTemplateParam( $lhs_type_part->param_name, $lhs_type_part->as_type - ? new Type\Union([$lhs_type_part->as_type]) + ? new Union([$lhs_type_part->as_type]) : Type::getObject(), $lhs_type_part->defining_class ); @@ -680,7 +696,7 @@ private static function analyzeConstructorExpression( ); } - $new_type = Type::combineUnionTypes($new_type, new Type\Union([$new_type_part])); + $new_type = Type::combineUnionTypes($new_type, new Union([$new_type_part])); if ($lhs_type_part->as_type && $codebase->classlikes->classExists($lhs_type_part->as_type->value) @@ -720,15 +736,15 @@ private static function analyzeConstructorExpression( continue; } - if ($lhs_type_part instanceof Type\Atomic\TLiteralClassString - || $lhs_type_part instanceof Type\Atomic\TClassString - || $lhs_type_part instanceof Type\Atomic\TDependentGetClass + if ($lhs_type_part instanceof TLiteralClassString + || $lhs_type_part instanceof TClassString + || $lhs_type_part instanceof TDependentGetClass ) { if (!$statements_analyzer->node_data->getType($stmt)) { - if ($lhs_type_part instanceof Type\Atomic\TClassString) { + if ($lhs_type_part instanceof TClassString) { $generated_type = $lhs_type_part->as_type ? clone $lhs_type_part->as_type - : new Type\Atomic\TObject(); + : new TObject(); if ($lhs_type_part->as_type && $codebase->classlikes->classExists($lhs_type_part->as_type->value) @@ -749,31 +765,31 @@ private static function analyzeConstructorExpression( ); } } - } elseif ($lhs_type_part instanceof Type\Atomic\TDependentGetClass) { - $generated_type = new Type\Atomic\TObject(); + } elseif ($lhs_type_part instanceof TDependentGetClass) { + $generated_type = new TObject(); if ($lhs_type_part->as_type->hasObjectType() && $lhs_type_part->as_type->isSingle() ) { foreach ($lhs_type_part->as_type->getAtomicTypes() as $typeof_type_atomic) { - if ($typeof_type_atomic instanceof Type\Atomic\TNamedObject) { - $generated_type = new Type\Atomic\TNamedObject( + if ($typeof_type_atomic instanceof TNamedObject) { + $generated_type = new TNamedObject( $typeof_type_atomic->value ); } } } } else { - $generated_type = new Type\Atomic\TNamedObject( + $generated_type = new TNamedObject( $lhs_type_part->value ); } - if ($lhs_type_part instanceof Type\Atomic\TClassString) { + if ($lhs_type_part instanceof TClassString) { $can_extend = true; } - if ($generated_type instanceof Type\Atomic\TObject) { + if ($generated_type instanceof TObject) { IssueBuffer::maybeAdd( new MixedMethodCall( 'Cannot call constructor on an unknown class', @@ -783,15 +799,15 @@ private static function analyzeConstructorExpression( ); } - $new_type = Type::combineUnionTypes($new_type, new Type\Union([$generated_type])); + $new_type = Type::combineUnionTypes($new_type, new Union([$generated_type])); } continue; } - if ($lhs_type_part instanceof Type\Atomic\TString) { + if ($lhs_type_part instanceof TString) { if ($config->allow_string_standin_for_class - && !$lhs_type_part instanceof Type\Atomic\TNumericString + && !$lhs_type_part instanceof TNumericString ) { // do nothing } elseif (IssueBuffer::accepts( @@ -803,7 +819,7 @@ private static function analyzeConstructorExpression( )) { // fall through } - } elseif ($lhs_type_part instanceof Type\Atomic\TMixed) { + } elseif ($lhs_type_part instanceof TMixed) { IssueBuffer::maybeAdd( new MixedMethodCall( 'Cannot call constructor on an unknown class', @@ -811,11 +827,11 @@ private static function analyzeConstructorExpression( ), $statements_analyzer->getSuppressedIssues() ); - } elseif ($lhs_type_part instanceof Type\Atomic\TFalse + } elseif ($lhs_type_part instanceof TFalse && $stmt_class_type->ignore_falsable_issues ) { // do nothing - } elseif ($lhs_type_part instanceof Type\Atomic\TNull + } elseif ($lhs_type_part instanceof TNull && $stmt_class_type->ignore_nullable_issues ) { // do nothing diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php index 32ecb344e11..da43d41f250 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php @@ -1,4 +1,5 @@ inside_general_use; @@ -208,7 +211,7 @@ public static function analyze( $has_existing_method = false; foreach ($lhs_type->getAtomicTypes() as $lhs_type_part) { - StaticMethod\AtomicStaticCallAnalyzer::analyze( + AtomicStaticCallAnalyzer::analyze( $statements_analyzer, $stmt, $context, @@ -247,7 +250,7 @@ public static function taintReturnType( PhpParser\Node\Expr\StaticCall $stmt, MethodIdentifier $method_id, string $cased_method_id, - Type\Union $return_type_candidate, + Union $return_type_candidate, ?MethodStorage $method_storage, ?TemplateResult $template_result, ?Context $context = null 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 9ea98bdebe1..6adf64c5936 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php @@ -1,4 +1,5 @@ extra_types; - } elseif ($lhs_type_part instanceof Type\Atomic\TClassString + } elseif ($lhs_type_part instanceof TClassString && $lhs_type_part->as_type ) { $fq_class_name = $lhs_type_part->as_type->value; @@ -103,7 +117,7 @@ public static function analyze( } $intersection_types = $lhs_type_part->as_type->extra_types; - } elseif ($lhs_type_part instanceof Type\Atomic\TDependentGetClass + } elseif ($lhs_type_part instanceof TDependentGetClass && !$lhs_type_part->as_type->hasObject() ) { $fq_class_name = 'object'; @@ -112,7 +126,7 @@ public static function analyze( && $lhs_type_part->as_type->isSingle() ) { foreach ($lhs_type_part->as_type->getAtomicTypes() as $typeof_type_atomic) { - if ($typeof_type_atomic instanceof Type\Atomic\TNamedObject) { + if ($typeof_type_atomic instanceof TNamedObject) { $fq_class_name = $typeof_type_atomic->value; } } @@ -121,7 +135,7 @@ public static function analyze( if ($fq_class_name === 'object') { return; } - } elseif ($lhs_type_part instanceof Type\Atomic\TLiteralClassString) { + } elseif ($lhs_type_part instanceof TLiteralClassString) { $fq_class_name = $lhs_type_part->value; if (!ClassLikeAnalyzer::checkFullyQualifiedClassLikeName( @@ -134,7 +148,7 @@ public static function analyze( )) { return; } - } elseif ($lhs_type_part instanceof Type\Atomic\TTemplateParam + } elseif ($lhs_type_part instanceof TTemplateParam && !$lhs_type_part->as->isMixed() && !$lhs_type_part->as->hasObject() ) { @@ -249,7 +263,7 @@ private static function handleNamedCall( PhpParser\Node\Expr\StaticCall $stmt, PhpParser\Node\Identifier $stmt_name, Context $context, - Type\Atomic $lhs_type_part, + Atomic $lhs_type_part, array $intersection_types, string $fq_class_name, bool &$moved_call, @@ -366,17 +380,17 @@ private static function handleNamedCall( } $mixin_candidates_no_generic = array_filter($mixin_candidates, function ($check): bool { - return !($check instanceof Type\Atomic\TGenericObject); + return !($check instanceof TGenericObject); }); // $mixin_candidates_no_generic will only be empty when there are TGenericObject entries. // In that case, Union will be initialized with an empty array but // replaced with non-empty types in the following loop. /** @psalm-suppress ArgumentTypeCoercion */ - $mixin_candidate_type = new Type\Union($mixin_candidates_no_generic); + $mixin_candidate_type = new Union($mixin_candidates_no_generic); foreach ($mixin_candidates as $tGenericMixin) { - if (!($tGenericMixin instanceof Type\Atomic\TGenericObject)) { + if (!($tGenericMixin instanceof TGenericObject)) { continue; } @@ -386,7 +400,7 @@ private static function handleNamedCall( $new_mixin_candidate_type = AtomicPropertyFetchAnalyzer::localizePropertyType( $codebase, - new Type\Union([$lhs_type_part]), + new Union([$lhs_type_part]), $tGenericMixin, $class_storage, $mixin_declaring_class_storage @@ -780,7 +794,7 @@ function (PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem { $method_storage = ($class_storage->methods[$method_id->method_name] ?? null); if ($method_storage) { - $return_type_candidate = new Type\Union([new Type\Atomic\TClosure( + $return_type_candidate = new Union([new TClosure( 'Closure', $method_storage->params, $method_storage->return_type, @@ -925,16 +939,16 @@ public static function handleNonObjectCall( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\StaticCall $stmt, Context $context, - Type\Atomic $lhs_type_part, + Atomic $lhs_type_part, bool $ignore_nullable_issues ): void { $codebase = $statements_analyzer->getCodebase(); $config = $codebase->config; - if ($lhs_type_part instanceof Type\Atomic\TMixed - || $lhs_type_part instanceof Type\Atomic\TTemplateParam - || $lhs_type_part instanceof Type\Atomic\TClassString - || $lhs_type_part instanceof Type\Atomic\TObject + if ($lhs_type_part instanceof TMixed + || $lhs_type_part instanceof TTemplateParam + || $lhs_type_part instanceof TClassString + || $lhs_type_part instanceof TObject ) { if ($stmt->name instanceof PhpParser\Node\Identifier) { $codebase->analyzer->addMixedMemberName( @@ -954,9 +968,9 @@ public static function handleNonObjectCall( return; } - if ($lhs_type_part instanceof Type\Atomic\TString) { + if ($lhs_type_part instanceof TString) { if ($config->allow_string_standin_for_class - && !$lhs_type_part instanceof Type\Atomic\TNumericString + && !$lhs_type_part instanceof TNumericString ) { return; } @@ -972,7 +986,7 @@ public static function handleNonObjectCall( return; } - if ($lhs_type_part instanceof Type\Atomic\TNull + if ($lhs_type_part instanceof TNull && $ignore_nullable_issues ) { return; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php index d55263db206..6951396f72a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php @@ -1,4 +1,5 @@ getAtomicTypes() as $t) { - if ($t instanceof Type\Atomic\TTemplateParam + if ($t instanceof TTemplateParam && isset($found_generic_params[$t->param_name][$t->defining_class]) ) { $found_generic_params[$type_key][$template_fq_class_name] @@ -256,11 +262,11 @@ public static function analyze( || !UnionTypeComparator::isContainedBy( $codebase, $context->vars_in_scope['$this'] - ?? new Type\Union([ - new Type\Atomic\TNamedObject($context->self) + ?? new Union([ + new TNamedObject($context->self) ]), - new Type\Union([ - new Type\Atomic\TNamedObject($method_id->fq_class_name) + new Union([ + new TNamedObject($method_id->fq_class_name) ]) )) ) { @@ -461,12 +467,12 @@ private static function getMethodReturnType( array $args, TemplateResult $template_result, ?string &$self_fq_class_name, - Type\Atomic $lhs_type_part, + Atomic $lhs_type_part, Context $context, string $fq_class_name, ClassLikeStorage $class_storage, Config $config - ): ?Type\Union { + ): ?Union { $return_type_candidate = $codebase->methods->getMethodReturnType( $method_id, $self_fq_class_name, @@ -527,20 +533,20 @@ private static function getMethodReturnType( $context_final = false; - if ($lhs_type_part instanceof Type\Atomic\TTemplateParam) { + if ($lhs_type_part instanceof TTemplateParam) { $static_type = $lhs_type_part; - } elseif ($lhs_type_part instanceof Type\Atomic\TTemplateParamClass) { - $static_type = new Type\Atomic\TTemplateParam( + } elseif ($lhs_type_part instanceof TTemplateParamClass) { + $static_type = new TTemplateParam( $lhs_type_part->param_name, $lhs_type_part->as_type - ? new Type\Union([$lhs_type_part->as_type]) + ? new Union([$lhs_type_part->as_type]) : Type::getObject(), $lhs_type_part->defining_class ); } elseif ($stmt->class instanceof PhpParser\Node\Name && count($stmt->class->parts) === 1 && in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true) - && $lhs_type_part instanceof Type\Atomic\TNamedObject + && $lhs_type_part instanceof TNamedObject && $context->self ) { $static_type = $context->self; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index 7d1fbfb25f7..b40ec32f97d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -1,4 +1,5 @@ getCodebase(); if (!$method_id) { - return Call\ArgumentsAnalyzer::analyze( + return ArgumentsAnalyzer::analyze( $statements_analyzer, $args, null, @@ -330,7 +341,7 @@ public static function checkMethodArgs( } } - if (Call\ArgumentsAnalyzer::analyze( + if (ArgumentsAnalyzer::analyze( $statements_analyzer, $args, $method_params, @@ -342,7 +353,7 @@ public static function checkMethodArgs( return false; } - if (Call\ArgumentsAnalyzer::checkArgumentsMatch( + if (ArgumentsAnalyzer::checkArgumentsMatch( $statements_analyzer, $args, $method_id, @@ -372,9 +383,9 @@ public static function checkMethodArgs( * This gets all the template params (and their types) that we think * we'll need to know about * - * @return array> - * @param array> $existing_template_types - * @param array> $class_template_params + * @return array> + * @param array> $existing_template_types + * @param array> $class_template_params */ public static function getTemplateTypesForCall( Codebase $codebase, @@ -397,7 +408,7 @@ public static function getTemplateTypesForCall( $output_type = null; foreach ($type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TTemplateParam) { + if ($atomic_type instanceof TTemplateParam) { $output_type_candidate = self::getGenericParamForOffset( $atomic_type->defining_class, $atomic_type->param_name, @@ -405,7 +416,7 @@ public static function getTemplateTypesForCall( $class_template_params + $template_types ); } else { - $output_type_candidate = new Type\Union([$atomic_type]); + $output_type_candidate = new Union([$atomic_type]); } $output_type = Type::combineUnionTypes( @@ -447,15 +458,15 @@ public static function getTemplateTypesForCall( } /** - * @param array> $template_extended_params - * @param array> $found_generic_params + * @param array> $template_extended_params + * @param array> $found_generic_params */ public static function getGenericParamForOffset( string $fq_class_name, string $template_name, array $template_extended_params, array $found_generic_params - ): Type\Union { + ): Union { if (isset($found_generic_params[$template_name][$fq_class_name])) { return $found_generic_params[$template_name][$fq_class_name]; } @@ -463,7 +474,7 @@ public static function getGenericParamForOffset( foreach ($template_extended_params as $extended_class_name => $type_map) { foreach ($type_map as $extended_template_name => $extended_type) { foreach ($extended_type->getAtomicTypes() as $extended_atomic_type) { - if ($extended_atomic_type instanceof Type\Atomic\TTemplateParam + if ($extended_atomic_type instanceof TTemplateParam && $extended_atomic_type->param_name === $template_name && $extended_atomic_type->defining_class === $fq_class_name ) { @@ -571,8 +582,8 @@ public static function getFunctionIdsFromCallableArg( if ($type_part->extra_types) { foreach ($type_part->extra_types as $extra_type) { - if ($extra_type instanceof Type\Atomic\TTemplateParam - || $extra_type instanceof Type\Atomic\TObjectWithProperties + if ($extra_type instanceof TTemplateParam + || $extra_type instanceof TObjectWithProperties ) { throw new UnexpectedValueException('Shouldn’t get a generic param here'); } @@ -778,22 +789,22 @@ public static function applyAssertionsToContext( $ored_type_assertions = []; foreach ($replacement_atomic_types as $replacement_atomic_type) { - if ($replacement_atomic_type instanceof Type\Atomic\TMixed) { + if ($replacement_atomic_type instanceof TMixed) { continue 3; } - if ($replacement_atomic_type instanceof Type\Atomic\TArray - || $replacement_atomic_type instanceof Type\Atomic\TKeyedArray - || $replacement_atomic_type instanceof Type\Atomic\TList + if ($replacement_atomic_type instanceof TArray + || $replacement_atomic_type instanceof TKeyedArray + || $replacement_atomic_type instanceof TList ) { $ored_type_assertions[] = $prefix . $replacement_atomic_type->getId(); - } elseif ($replacement_atomic_type instanceof Type\Atomic\TNamedObject) { + } elseif ($replacement_atomic_type instanceof TNamedObject) { $ored_type_assertions[] = $prefix . $replacement_atomic_type->value; - } elseif ($replacement_atomic_type instanceof Type\Atomic\Scalar) { + } elseif ($replacement_atomic_type instanceof Scalar) { $ored_type_assertions[] = $prefix . $replacement_atomic_type->getAssertionString(); - } elseif ($replacement_atomic_type instanceof Type\Atomic\TNull) { + } elseif ($replacement_atomic_type instanceof TNull) { $ored_type_assertions[] = $prefix . 'null'; - } elseif ($replacement_atomic_type instanceof Type\Atomic\TTemplateParam) { + } elseif ($replacement_atomic_type instanceof TTemplateParam) { $ored_type_assertions[] = $prefix . $replacement_atomic_type->param_name; } } @@ -897,8 +908,8 @@ function ($bounds) use ($codebase) { foreach (($statements_analyzer->getTemplateTypeMap() ?: []) as $template_name => $map) { foreach ($map as $ref => $type) { - $template_type_map[$template_name][$ref] = new Type\Union([ - new Type\Atomic\TTemplateParam( + $template_type_map[$template_name][$ref] = new Union([ + new TTemplateParam( $template_name, $type, $ref @@ -961,7 +972,7 @@ function ($bounds) use ($codebase) { foreach ($op_vars_in_scope[$var_id]->getAtomicTypes() as $changed_atomic_type) { $changed_atomic_type->from_docblock = true; - if ($changed_atomic_type instanceof Type\Atomic\TNamedObject + if ($changed_atomic_type instanceof TNamedObject && $changed_atomic_type->extra_types ) { foreach ($changed_atomic_type->extra_types as $extra_type) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index 926a537089c..4f80b23e0a7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -1,4 +1,5 @@ getAtomicTypes()) === 1 - && $maybe_type->getSingleAtomic() instanceof Type\Atomic\TBool) { + && $maybe_type->getSingleAtomic() instanceof TBool) { $as_int = false; - $type = new Type\Union([ - new Type\Atomic\TLiteralInt(0), - new Type\Atomic\TLiteralInt(1), + $type = new Union([ + new TLiteralInt(0), + new TLiteralInt(1), ]); if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph @@ -173,11 +186,13 @@ public static function analyze( $was_inside_general_use = $context->inside_general_use; $context->inside_general_use = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return false; } $context->inside_general_use = $was_inside_general_use; - $type = new Type\Union([new TNamedObject('stdClass')]); + $type = new Union([new TNamedObject('stdClass')]); $maybe_type = $statements_analyzer->node_data->getType($stmt->expr); @@ -195,6 +210,8 @@ public static function analyze( $was_inside_general_use = $context->inside_general_use; $context->inside_general_use = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return false; } $context->inside_general_use = $was_inside_general_use; @@ -211,7 +228,7 @@ public static function analyze( foreach ($stmt_expr_type->getAtomicTypes() as $type) { if ($type instanceof Scalar) { - $keyed_array = new TKeyedArray([new Type\Union([$type])]); + $keyed_array = new TKeyedArray([new Union([$type])]); $keyed_array->is_list = true; $permissible_atomic_types[] = $keyed_array; } elseif ($type instanceof TNull) { @@ -269,10 +286,10 @@ public static function analyze( public static function castStringAttempt( StatementsAnalyzer $statements_analyzer, Context $context, - Type\Union $stmt_type, + Union $stmt_type, PhpParser\Node\Expr $stmt, bool $explicit_cast = false - ): Type\Union { + ): Union { $codebase = $statements_analyzer->getCodebase(); $invalid_casts = []; @@ -292,14 +309,14 @@ public static function castStringAttempt( if ($atomic_type instanceof TFloat || $atomic_type instanceof TInt - || $atomic_type instanceof Type\Atomic\TNumeric + || $atomic_type instanceof TNumeric ) { - if ($atomic_type instanceof Type\Atomic\TLiteralInt) { - $castable_types[] = new Type\Atomic\TLiteralString((string) $atomic_type->value); - } elseif ($atomic_type instanceof Type\Atomic\TNonspecificLiteralInt) { - $castable_types[] = new Type\Atomic\TNonspecificLiteralString(); + if ($atomic_type instanceof TLiteralInt) { + $castable_types[] = new TLiteralString((string) $atomic_type->value); + } elseif ($atomic_type instanceof TNonspecificLiteralInt) { + $castable_types[] = new TNonspecificLiteralString(); } else { - $castable_types[] = new Type\Atomic\TNumericString(); + $castable_types[] = new TNumericString(); } continue; @@ -312,15 +329,15 @@ public static function castStringAttempt( } if ($atomic_type instanceof TNull - || $atomic_type instanceof Type\Atomic\TFalse + || $atomic_type instanceof TFalse ) { - $valid_strings[] = new Type\Atomic\TLiteralString(''); + $valid_strings[] = new TLiteralString(''); continue; } if ($atomic_type instanceof TMixed - || $atomic_type instanceof Type\Atomic\TResource - || $atomic_type instanceof Type\Atomic\Scalar + || $atomic_type instanceof TResource + || $atomic_type instanceof Scalar ) { $castable_types[] = new TString(); @@ -328,7 +345,7 @@ public static function castStringAttempt( } if ($atomic_type instanceof TNamedObject - || $atomic_type instanceof Type\Atomic\TObjectWithProperties + || $atomic_type instanceof TObjectWithProperties ) { $intersection_types = [$atomic_type]; @@ -380,7 +397,7 @@ public static function castStringAttempt( } } - if ($intersection_type instanceof Type\Atomic\TObjectWithProperties + if ($intersection_type instanceof TObjectWithProperties && isset($intersection_type->methods['__toString']) ) { $castable_types[] = new TString(); @@ -390,7 +407,7 @@ public static function castStringAttempt( } } - if ($atomic_type instanceof Type\Atomic\TTemplateParam) { + if ($atomic_type instanceof TTemplateParam) { $atomic_types = array_merge($atomic_types, $atomic_type->as->getAtomicTypes()); continue; @@ -440,7 +457,7 @@ public static function castStringAttempt( } private static function handleRedundantCast( - Type\Union $maybe_type, + Union $maybe_type, StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\Cast $stmt ): void { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php index a1cb1a24327..45ed9e9a7f5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php @@ -1,4 +1,5 @@ as->getAtomicTypes()); } else { - if ($clone_type_part instanceof Type\Atomic\TFalse + if ($clone_type_part instanceof TFalse && $clone_type->ignore_falsable_issues ) { continue; } - if ($clone_type_part instanceof Type\Atomic\TNull + if ($clone_type_part instanceof TNull && $clone_type->ignore_nullable_issues ) { continue; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php index e93460bb5d7..e874371ff57 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php @@ -1,4 +1,5 @@ parent_nodes = $stmt_type->parent_nodes; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php index dddcf65f171..c8df04d5820 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php @@ -1,4 +1,5 @@ taints = [ - Type\TaintKind::INPUT_HTML, - Type\TaintKind::INPUT_HAS_QUOTES, - Type\TaintKind::USER_SECRET, - Type\TaintKind::SYSTEM_SECRET + TaintKind::INPUT_HTML, + TaintKind::INPUT_HAS_QUOTES, + TaintKind::USER_SECRET, + TaintKind::SYSTEM_SECRET ]; $statements_analyzer->data_flow_graph->addSink($echo_param_sink); @@ -88,7 +91,7 @@ public static function analyze( if (ArgumentAnalyzer::verifyType( $statements_analyzer, $expr_type, - new Type\Union([new TInt(), new TString()]), + new Union([new TInt(), new TString()]), null, 'exit', null, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php index 78edf7b0b55..19cbe304479 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php @@ -1,4 +1,5 @@ inside_unset = false; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->dim, $context) === false) { + $context->inside_unset = $was_inside_unset; + $context->inside_general_use = $was_inside_general_use; + return false; } @@ -207,11 +225,11 @@ public static function analyze( if ($stmt->dim && $stmt_var_type->hasArray()) { /** * @psalm-suppress PossiblyUndefinedStringArrayOffset - * @var TArray|TKeyedArray|TList|Type\Atomic\TClassStringMap + * @var TArray|TKeyedArray|TList|TClassStringMap */ $array_type = $stmt_var_type->getAtomicTypes()['array']; - if ($array_type instanceof Type\Atomic\TClassStringMap) { + if ($array_type instanceof TClassStringMap) { $array_value_type = Type::getMixed(); } elseif ($array_type instanceof TArray) { $array_value_type = $array_type->type_params[1]; @@ -263,7 +281,7 @@ public static function analyze( if (!isset($const_array_key_atomic_types[$offset_key]) && !UnionTypeComparator::isContainedBy( $codebase, - new Type\Union([$offset_atomic_type]), + new Union([$offset_atomic_type]), $const_array_key_type ) ) { @@ -272,7 +290,7 @@ public static function analyze( } elseif (!UnionTypeComparator::isContainedBy( $codebase, $const_array_key_type, - new Type\Union([$offset_atomic_type]) + new Union([$offset_atomic_type]) )) { $new_offset_type->removeType($offset_key); } @@ -342,8 +360,8 @@ public static function taintArrayFetch( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr $var, ?string $keyed_array_var_id, - Type\Union $stmt_type, - Type\Union $offset_type, + Union $stmt_type, + Union $offset_type, ?Context $context = null ): void { if ($statements_analyzer->data_flow_graph @@ -439,14 +457,14 @@ public static function taintArrayFetch( public static function getArrayAccessTypeGivenOffset( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\ArrayDimFetch $stmt, - Type\Union $array_type, - Type\Union $offset_type, + Union $array_type, + Union $offset_type, bool $in_assignment, ?string $array_var_id, Context $context, PhpParser\Node\Expr $assign_value = null, - Type\Union $replacement_type = null - ): Type\Union { + Union $replacement_type = null + ): Union { $codebase = $statements_analyzer->getCodebase(); $has_array_access = false; @@ -574,7 +592,7 @@ public static function getArrayAccessTypeGivenOffset( $statements_analyzer->getSuppressedIssues() ); - $array_access_type = new Type\Union([new TEmpty]); + $array_access_type = new Union([new TEmpty]); } } else { if (!$context->inside_isset && !MethodCallAnalyzer::hasNullsafe($stmt->var)) { @@ -650,7 +668,7 @@ public static function getArrayAccessTypeGivenOffset( $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath()); } - if ($type instanceof Type\Atomic\TFalse && $array_type->ignore_falsable_issues) { + if ($type instanceof TFalse && $array_type->ignore_falsable_issues) { continue; } @@ -777,31 +795,31 @@ public static function getArrayAccessTypeGivenOffset( $good_types = []; $bad_types = []; foreach ($offset_type->getAtomicTypes() as $atomic_key_type) { - if (!$atomic_key_type instanceof Type\Atomic\TString - && !$atomic_key_type instanceof Type\Atomic\TInt - && !$atomic_key_type instanceof Type\Atomic\TArrayKey - && !$atomic_key_type instanceof Type\Atomic\TMixed - && !$atomic_key_type instanceof Type\Atomic\TTemplateParam + if (!$atomic_key_type instanceof TString + && !$atomic_key_type instanceof TInt + && !$atomic_key_type instanceof TArrayKey + && !$atomic_key_type instanceof TMixed + && !$atomic_key_type instanceof TTemplateParam && !( - $atomic_key_type instanceof Type\Atomic\TObjectWithProperties + $atomic_key_type instanceof TObjectWithProperties && isset($atomic_key_type->methods['__toString']) ) ) { $bad_types[] = $atomic_key_type; - if ($atomic_key_type instanceof Type\Atomic\TFalse) { - $good_types[] = new Type\Atomic\TLiteralInt(0); - } elseif ($atomic_key_type instanceof Type\Atomic\TTrue) { - $good_types[] = new Type\Atomic\TLiteralInt(1); - } elseif ($atomic_key_type instanceof Type\Atomic\TBool) { - $good_types[] = new Type\Atomic\TLiteralInt(0); - $good_types[] = new Type\Atomic\TLiteralInt(1); - } elseif ($atomic_key_type instanceof Type\Atomic\TLiteralFloat) { - $good_types[] = new Type\Atomic\TLiteralInt((int)$atomic_key_type->value); - } elseif ($atomic_key_type instanceof Type\Atomic\TFloat) { - $good_types[] = new Type\Atomic\TInt; + if ($atomic_key_type instanceof TFalse) { + $good_types[] = new TLiteralInt(0); + } elseif ($atomic_key_type instanceof TTrue) { + $good_types[] = new TLiteralInt(1); + } elseif ($atomic_key_type instanceof TBool) { + $good_types[] = new TLiteralInt(0); + $good_types[] = new TLiteralInt(1); + } elseif ($atomic_key_type instanceof TLiteralFloat) { + $good_types[] = new TLiteralInt((int)$atomic_key_type->value); + } elseif ($atomic_key_type instanceof TFloat) { + $good_types[] = new TInt; } else { - $good_types[] = new Type\Atomic\TArrayKey; + $good_types[] = new TArrayKey; } } } @@ -842,8 +860,8 @@ public static function getArrayAccessTypeGivenOffset( } private static function checkLiteralIntArrayOffset( - Type\Union $offset_type, - Type\Union $expected_offset_type, + Union $offset_type, + Union $expected_offset_type, ?string $array_var_id, PhpParser\Node\Expr\ArrayDimFetch $stmt, Context $context, @@ -872,7 +890,7 @@ private static function checkLiteralIntArrayOffset( break; } - if ($offset_type_part instanceof Type\Atomic\TPositiveInt) { + if ($offset_type_part instanceof TPositiveInt) { $found_match = true; break; } @@ -895,8 +913,8 @@ private static function checkLiteralIntArrayOffset( } private static function checkLiteralStringArrayOffset( - Type\Union $offset_type, - Type\Union $expected_offset_type, + Union $offset_type, + Union $expected_offset_type, ?string $array_var_id, PhpParser\Node\Expr\ArrayDimFetch $stmt, Context $context, @@ -942,39 +960,39 @@ private static function checkLiteralStringArrayOffset( } } - public static function replaceOffsetTypeWithInts(Type\Union $offset_type): Type\Union + public static function replaceOffsetTypeWithInts(Union $offset_type): Union { $offset_types = $offset_type->getAtomicTypes(); $cloned = false; foreach ($offset_types as $key => $offset_type_part) { - if ($offset_type_part instanceof Type\Atomic\TLiteralString) { + if ($offset_type_part instanceof TLiteralString) { if (preg_match('/^(0|[1-9][0-9]*)$/', $offset_type_part->value)) { if (!$cloned) { $offset_type = clone $offset_type; $cloned = true; } - $offset_type->addType(new Type\Atomic\TLiteralInt((int) $offset_type_part->value)); + $offset_type->addType(new TLiteralInt((int) $offset_type_part->value)); $offset_type->removeType($key); } - } elseif ($offset_type_part instanceof Type\Atomic\TBool) { + } elseif ($offset_type_part instanceof TBool) { if (!$cloned) { $offset_type = clone $offset_type; $cloned = true; } - if ($offset_type_part instanceof Type\Atomic\TFalse) { + if ($offset_type_part instanceof TFalse) { if (!$offset_type->ignore_falsable_issues) { - $offset_type->addType(new Type\Atomic\TLiteralInt(0)); + $offset_type->addType(new TLiteralInt(0)); $offset_type->removeType($key); } - } elseif ($offset_type_part instanceof Type\Atomic\TTrue) { - $offset_type->addType(new Type\Atomic\TLiteralInt(1)); + } elseif ($offset_type_part instanceof TTrue) { + $offset_type->addType(new TLiteralInt(1)); $offset_type->removeType($key); } else { - $offset_type->addType(new Type\Atomic\TLiteralInt(0)); - $offset_type->addType(new Type\Atomic\TLiteralInt(1)); + $offset_type->addType(new TLiteralInt(0)); + $offset_type->addType(new TLiteralInt(1)); $offset_type->removeType($key); } } @@ -984,7 +1002,7 @@ public static function replaceOffsetTypeWithInts(Type\Union $offset_type): Type\ } /** - * @param Type\Atomic\TMixed|Type\Atomic\TTemplateParam|Type\Atomic\TEmpty $type + * @param TMixed|TTemplateParam|TEmpty $type */ public static function handleMixedArrayAccess( Context $context, @@ -993,9 +1011,9 @@ public static function handleMixedArrayAccess( bool $in_assignment, ?string $array_var_id, PhpParser\Node\Expr\ArrayDimFetch $stmt, - ?Type\Union $array_access_type, - Type\Atomic $type - ): Type\Union { + ?Union $array_access_type, + Atomic $type + ): Union { if (!$context->collect_initializations && !$context->collect_mutations && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath() @@ -1061,25 +1079,25 @@ public static function handleMixedArrayAccess( /** * @param list $expected_offset_types - * @param Type\Atomic\TArray|Type\Atomic\TKeyedArray|Type\Atomic\TList|Type\Atomic\TClassStringMap $type + * @param TArray|TKeyedArray|TList|TClassStringMap $type * @param list $key_values */ private static function handleArrayAccessOnArray( bool $in_assignment, - Type\Atomic &$type, + Atomic &$type, array &$key_values, - Type\Union $array_type, + Union $array_type, string $type_string, PhpParser\Node\Expr\ArrayDimFetch $stmt, - ?Type\Union $replacement_type, - Type\Union &$offset_type, - Type\Atomic $original_type, + ?Union $replacement_type, + Union &$offset_type, + Atomic $original_type, Codebase $codebase, ?string $array_var_id, Context $context, StatementsAnalyzer $statements_analyzer, array &$expected_offset_types, - ?Type\Union &$array_access_type, + ?Union &$array_access_type, bool &$has_array_access, bool &$has_valid_offset ): void { @@ -1113,7 +1131,7 @@ private static function handleArrayAccessOnArray( $array_type->addType($type); } elseif (!$stmt->dim && $from_empty_array && $replacement_type) { $array_type->removeType($type_string); - $array_type->addType(new Type\Atomic\TNonEmptyList($replacement_type)); + $array_type->addType(new TNonEmptyList($replacement_type)); return; } } elseif ($in_assignment @@ -1209,15 +1227,15 @@ private static function handleArrayAccessOnTArray( Codebase $codebase, Context $context, PhpParser\Node\Expr\ArrayDimFetch $stmt, - Type\Union $array_type, + Union $array_type, ?string $array_var_id, TArray $type, - Type\Union $offset_type, + Union $offset_type, bool $in_assignment, array &$expected_offset_types, - ?Type\Union $replacement_type, - ?Type\Union &$array_access_type, - Type\Atomic $original_type, + ?Union $replacement_type, + ?Union &$array_access_type, + Atomic $original_type, bool &$has_valid_offset ): void { // if we're assigning to an empty array with a key offset, refashion that array @@ -1229,7 +1247,7 @@ private static function handleArrayAccessOnTArray( } } elseif (!$type->type_params[0]->isEmpty()) { $expected_offset_type = $type->type_params[0]->hasMixed() - ? new Type\Union([new TArrayKey]) + ? new Union([new TArrayKey]) : $type->type_params[0]; $templated_offset_type = null; @@ -1244,12 +1262,12 @@ private static function handleArrayAccessOnTArray( if ($original_type instanceof TTemplateParam && $templated_offset_type) { foreach ($templated_offset_type->as->getAtomicTypes() as $offset_as) { - if ($offset_as instanceof Type\Atomic\TTemplateKeyOf + if ($offset_as instanceof TTemplateKeyOf && $offset_as->param_name === $original_type->param_name && $offset_as->defining_class === $original_type->defining_class ) { - $type->type_params[1] = new Type\Union([ - new Type\Atomic\TTemplateIndexedAccess( + $type->type_params[1] = new Union([ + new TTemplateIndexedAccess( $offset_as->param_name, $templated_offset_type->param_name, $offset_as->defining_class @@ -1369,25 +1387,25 @@ private static function handleArrayAccessOnTArray( private static function handleArrayAccessOnClassStringMap( Codebase $codebase, - Type\Atomic\TClassStringMap $type, - Type\Union $offset_type, - ?Type\Union $replacement_type, - ?Type\Union &$array_access_type + TClassStringMap $type, + Union $offset_type, + ?Union $replacement_type, + ?Union &$array_access_type ): void { $offset_type_parts = array_values($offset_type->getAtomicTypes()); foreach ($offset_type_parts as $offset_type_part) { - if ($offset_type_part instanceof Type\Atomic\TClassString) { - if ($offset_type_part instanceof Type\Atomic\TTemplateParamClass) { + if ($offset_type_part instanceof TClassString) { + if ($offset_type_part instanceof TTemplateParamClass) { $template_result_get = new TemplateResult( [], [ $type->param_name => [ - 'class-string-map' => new Type\Union([ + 'class-string-map' => new Union([ new TTemplateParam( $offset_type_part->param_name, $offset_type_part->as_type - ? new Type\Union([$offset_type_part->as_type]) + ? new Union([$offset_type_part->as_type]) : Type::getObject(), $offset_type_part->defining_class ) @@ -1400,11 +1418,11 @@ private static function handleArrayAccessOnClassStringMap( [], [ $offset_type_part->param_name => [ - $offset_type_part->defining_class => new Type\Union([ + $offset_type_part->defining_class => new Union([ new TTemplateParam( $type->param_name, $type->as_type - ? new Type\Union([$type->as_type]) + ? new Union([$type->as_type]) : Type::getObject(), 'class-string-map' ) @@ -1417,9 +1435,9 @@ private static function handleArrayAccessOnClassStringMap( [], [ $type->param_name => [ - 'class-string-map' => new Type\Union([ + 'class-string-map' => new Union([ $offset_type_part->as_type - ?: new Type\Atomic\TObject() + ?: new TObject() ]) ] ] @@ -1471,15 +1489,15 @@ private static function handleArrayAccessOnKeyedArray( StatementsAnalyzer $statements_analyzer, Codebase $codebase, array &$key_values, - ?Type\Union $replacement_type, - ?Type\Union &$array_access_type, + ?Union $replacement_type, + ?Union &$array_access_type, bool $in_assignment, PhpParser\Node\Expr\ArrayDimFetch $stmt, - Type\Union $offset_type, + Union $offset_type, ?string $array_var_id, Context $context, - Type\Atomic\TKeyedArray $type, - Type\Union $array_type, + TKeyedArray $type, + Union $array_type, array &$expected_offset_types, string $type_string, bool &$has_valid_offset @@ -1507,7 +1525,7 @@ private static function handleArrayAccessOnKeyedArray( clone $type->properties[$key_value] ); } elseif ($in_assignment) { - $type->properties[$key_value] = new Type\Union([new TEmpty]); + $type->properties[$key_value] = new Union([new TEmpty]); $array_access_type = Type::combineUnionTypes( $array_access_type, @@ -1675,14 +1693,14 @@ private static function handleArrayAccessOnList( Codebase $codebase, PhpParser\Node\Expr\ArrayDimFetch $stmt, TList $type, - Type\Union $offset_type, + Union $offset_type, ?string $array_var_id, array $key_values, Context $context, bool $in_assignment, array &$expected_offset_types, - ?Type\Union $replacement_type, - ?Type\Union &$array_access_type, + ?Union $replacement_type, + ?Union &$array_access_type, bool &$has_valid_offset ): void { // if we're assigning to an empty array with a key offset, refashion that array @@ -1717,7 +1735,7 @@ private static function handleArrayAccessOnList( } } - if ($in_assignment && $type instanceof Type\Atomic\TNonEmptyList && $type->count !== null) { + if ($in_assignment && $type instanceof TNonEmptyList && $type->count !== null) { $type->count++; } @@ -1742,11 +1760,11 @@ private static function handleArrayAccessOnNamedObject( Context $context, bool $in_assignment, ?PhpParser\Node\Expr $assign_value, - ?Type\Union &$array_access_type, + ?Union &$array_access_type, bool &$has_array_access ): void { if (strtolower($type->value) === 'simplexmlelement') { - $call_array_access_type = new Type\Union([new TNamedObject('SimpleXMLElement')]); + $call_array_access_type = new Union([new TNamedObject('SimpleXMLElement')]); } elseif (strtolower($type->value) === 'domnodelist' && $stmt->dim) { $old_data_provider = $statements_analyzer->node_data; @@ -1887,11 +1905,11 @@ private static function handleArrayAccessOnString( PhpParser\Node\Expr\ArrayDimFetch $stmt, bool $in_assignment, Context $context, - ?Type\Union $replacement_type, + ?Union $replacement_type, TString $type, - Type\Union $offset_type, + Union $offset_type, array &$expected_offset_types, - ?Type\Union &$array_access_type, + ?Union &$array_access_type, bool &$has_valid_offset ): void { if ($in_assignment && $replacement_type) { @@ -1942,7 +1960,7 @@ private static function handleArrayAccessOnString( throw new UnexpectedValueException('This is weird'); } - $valid_offset_type = new Type\Union($valid_offsets); + $valid_offset_type = new Union($valid_offsets); } else { $valid_offset_type = Type::getInt(); } @@ -1973,13 +1991,13 @@ private static function handleArrayAccessOnString( * @param Atomic[] $offset_types */ private static function checkArrayOffsetType( - Type\Union $offset_type, + Union $offset_type, array $offset_types, Codebase $codebase ): bool { $has_valid_absolute_offset = false; foreach ($offset_types as $atomic_offset_type) { - if ($atomic_offset_type instanceof Type\Atomic\TClassConstant) { + if ($atomic_offset_type instanceof TClassConstant) { $expanded = TypeExpander::expandAtomic( $codebase, $atomic_offset_type, @@ -1991,7 +2009,7 @@ private static function checkArrayOffsetType( ); if ($expanded instanceof Atomic) { - if (!$expanded instanceof Atomic\TClassConstant) { + if (!$expanded instanceof TClassConstant) { $has_valid_absolute_offset = self::checkArrayOffsetType( $offset_type, [$expanded], @@ -2011,22 +2029,22 @@ private static function checkArrayOffsetType( } } - if ($atomic_offset_type instanceof Type\Atomic\TFalse && + if ($atomic_offset_type instanceof TFalse && $offset_type->ignore_falsable_issues === true ) { //do nothing - } elseif ($atomic_offset_type instanceof Type\Atomic\TNull && + } elseif ($atomic_offset_type instanceof TNull && $offset_type->ignore_nullable_issues === true ) { //do nothing - } elseif ($atomic_offset_type instanceof Type\Atomic\TString || - $atomic_offset_type instanceof Type\Atomic\TInt || - $atomic_offset_type instanceof Type\Atomic\TArrayKey || - $atomic_offset_type instanceof Type\Atomic\TMixed + } elseif ($atomic_offset_type instanceof TString || + $atomic_offset_type instanceof TInt || + $atomic_offset_type instanceof TArrayKey || + $atomic_offset_type instanceof TMixed ) { $has_valid_absolute_offset = true; break; - } elseif ($atomic_offset_type instanceof Type\Atomic\TTemplateParam) { + } elseif ($atomic_offset_type instanceof TTemplateParam) { $has_valid_absolute_offset = self::checkArrayOffsetType( $offset_type, $atomic_offset_type->as->getAtomicTypes(), diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php index 9f91801b4d2..93c440391c7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php @@ -1,4 +1,5 @@ node_data->setType($stmt, Type::getMixed()); return; } - if ($lhs_type_part instanceof Type\Atomic\TFalse && $stmt_var_type->ignore_falsable_issues) { + if ($lhs_type_part instanceof TFalse && $stmt_var_type->ignore_falsable_issues) { return; } @@ -181,25 +190,25 @@ public static function analyze( foreach ($class_storage->enum_cases as $enum_case) { if (is_string($enum_case->value)) { - $case_values[] = new Type\Atomic\TLiteralString($enum_case->value); + $case_values[] = new TLiteralString($enum_case->value); } elseif (is_int($enum_case->value)) { - $case_values[] = new Type\Atomic\TLiteralInt($enum_case->value); + $case_values[] = new TLiteralInt($enum_case->value); } else { // this should never happen - $case_values[] = new Type\Atomic\TMixed(); + $case_values[] = new TMixed(); } } // todo: this is suboptimal when we reference enum directly, e.g. Status::Open->value $statements_analyzer->node_data->setType( $stmt, - new Type\Union($case_values) + new Union($case_values) ); } elseif ($prop_name === 'name') { - if ($lhs_type_part instanceof Type\Atomic\TEnumCase) { + if ($lhs_type_part instanceof TEnumCase) { $statements_analyzer->node_data->setType( $stmt, - new Type\Union([new Type\Atomic\TLiteralString($lhs_type_part->case_name)]) + new Union([new TLiteralString($lhs_type_part->case_name)]) ); } else { $statements_analyzer->node_data->setType($stmt, Type::getNonEmptyString()); @@ -527,7 +536,7 @@ private static function propertyFetchCanBeAnalyzed( Context $context, string $fq_class_name, string $prop_name, - Type\Atomic\TNamedObject $lhs_type_part, + TNamedObject $lhs_type_part, string &$property_id, bool &$has_magic_getter, ?string $stmt_var_id, @@ -617,7 +626,7 @@ private static function propertyFetchCanBeAnalyzed( $statements_analyzer->node_data = clone $statements_analyzer->node_data; - $statements_analyzer->node_data->setType($stmt->var, new Type\Union([$lhs_type_part])); + $statements_analyzer->node_data->setType($stmt->var, new Union([$lhs_type_part])); $fake_method_call = new VirtualMethodCall( $stmt->var, @@ -694,11 +703,11 @@ private static function propertyFetchCanBeAnalyzed( public static function localizePropertyType( Codebase $codebase, - Type\Union $class_property_type, + Union $class_property_type, TGenericObject $lhs_type_part, ClassLikeStorage $property_class_storage, ClassLikeStorage $property_declaring_class_storage - ): Type\Union { + ): Union { $template_types = CallAnalyzer::getTemplateTypesForCall( $codebase, $property_declaring_class_storage, @@ -730,7 +739,7 @@ public static function localizePropertyType( $mapped_type = $extended_types[$property_declaring_class_storage->name][$type_name]; foreach ($mapped_type->getAtomicTypes() as $mapped_type_atomic) { - if (!$mapped_type_atomic instanceof Type\Atomic\TTemplateParam) { + if (!$mapped_type_atomic instanceof TTemplateParam) { continue; } @@ -766,7 +775,7 @@ public static function localizePropertyType( public static function processTaints( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\PropertyFetch $stmt, - Type\Union $type, + Union $type, string $property_id, ClassLikeStorage $class_storage, bool $in_assignment, @@ -969,7 +978,7 @@ private static function handleUndefinedProperty( } /** - * @param array $intersection_types + * @param array $intersection_types */ private static function handleNonExistentClass( StatementsAnalyzer $statements_analyzer, @@ -1125,7 +1134,7 @@ private static function getClassPropertyType( string $fq_class_name, string $prop_name, TNamedObject $lhs_type_part - ): Type\Union { + ): Union { $class_property_type = $codebase->properties->getPropertyType( $property_id, false, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php index 596710fe154..e6cb06ca161 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php @@ -1,4 +1,5 @@ was_static = true; $statements_analyzer->node_data->setType( $stmt, - new Type\Union([ - new Type\Atomic\TClassString($fq_class_name, $static_named_object) + new Union([ + new TClassString($fq_class_name, $static_named_object) ]) ); } else { @@ -211,6 +218,18 @@ public static function analyze( } $const_class_storage = $codebase->classlike_storage_provider->get($fq_class_name); + if ($const_class_storage->is_enum) { + $case = $const_class_storage->enum_cases[(string)$stmt->name] ?? null; + if ($case && $case->deprecated) { + IssueBuffer::maybeAdd( + new DeprecatedConstant( + "Enum Case $const_id is marked as deprecated", + new CodeLocation($statements_analyzer->getSource(), $stmt) + ), + $statements_analyzer->getSuppressedIssues() + ); + } + } if ($fq_class_name === $context->self || ( @@ -373,6 +392,8 @@ public static function analyze( $context->inside_general_use = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->class, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return false; } @@ -390,41 +411,41 @@ public static function analyze( $has_mixed_or_object = false; foreach ($lhs_type->getAtomicTypes() as $lhs_atomic_type) { - if ($lhs_atomic_type instanceof Type\Atomic\TNamedObject) { - $class_string_types[] = new Type\Atomic\TClassString( + if ($lhs_atomic_type instanceof TNamedObject) { + $class_string_types[] = new TClassString( $lhs_atomic_type->value, clone $lhs_atomic_type ); - } elseif ($lhs_atomic_type instanceof Type\Atomic\TTemplateParam + } elseif ($lhs_atomic_type instanceof TTemplateParam && $lhs_atomic_type->as->isSingle()) { $as_atomic_type = $lhs_atomic_type->as->getSingleAtomic(); - if ($as_atomic_type instanceof Type\Atomic\TObject) { - $class_string_types[] = new Type\Atomic\TTemplateParamClass( + if ($as_atomic_type instanceof TObject) { + $class_string_types[] = new TTemplateParamClass( $lhs_atomic_type->param_name, 'object', null, $lhs_atomic_type->defining_class ); } elseif ($as_atomic_type instanceof TNamedObject) { - $class_string_types[] = new Type\Atomic\TTemplateParamClass( + $class_string_types[] = new TTemplateParamClass( $lhs_atomic_type->param_name, $as_atomic_type->value, $as_atomic_type, $lhs_atomic_type->defining_class ); } - } elseif ($lhs_atomic_type instanceof Type\Atomic\TObject - || $lhs_atomic_type instanceof Type\Atomic\TMixed + } elseif ($lhs_atomic_type instanceof TObject + || $lhs_atomic_type instanceof TMixed ) { $has_mixed_or_object = true; } } if ($has_mixed_or_object) { - $statements_analyzer->node_data->setType($stmt, new Type\Union([new Type\Atomic\TClassString()])); + $statements_analyzer->node_data->setType($stmt, new Union([new TClassString()])); } elseif ($class_string_types) { - $statements_analyzer->node_data->setType($stmt, new Type\Union($class_string_types)); + $statements_analyzer->node_data->setType($stmt, new Union($class_string_types)); } return true; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php index 093eb86cd19..2d0d50e7bef 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php @@ -1,4 +1,5 @@ getAliases()->constants; if (isset($aliased_constants[$const_name])) { @@ -250,7 +252,7 @@ public static function getConstType( public static function setConstType( StatementsAnalyzer $statements_analyzer, string $const_name, - Type\Union $const_type, + Union $const_type, Context $context ): void { $context->vars_in_scope[$const_name] = $const_type; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php index e36586dceda..35fb44b1acd 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php @@ -1,4 +1,5 @@ var, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return false; } @@ -201,7 +205,7 @@ public static function analyze( if (!$prop_name) { if ($stmt_var_type->hasObjectType() && !$context->ignore_variable_property) { foreach ($stmt_var_type->getAtomicTypes() as $type) { - if ($type instanceof Type\Atomic\TNamedObject) { + if ($type instanceof TNamedObject) { $codebase->analyzer->addMixedMemberName( strtolower($type->value) . '::$', $context->calling_method_id ?: $statements_analyzer->getFileName() @@ -232,7 +236,7 @@ public static function analyze( $var_atomic_types = $stmt_var_type->getAtomicTypes(); while ($lhs_type_part = array_shift($var_atomic_types)) { - if ($lhs_type_part instanceof Type\Atomic\TTemplateParam) { + if ($lhs_type_part instanceof TTemplateParam) { $var_atomic_types = array_merge($var_atomic_types, $lhs_type_part->as->getAtomicTypes()); continue; } @@ -382,7 +386,7 @@ private static function handleScopedProperty( $statements_analyzer->getSuppressedIssues() ); - $stmt_type->addType(new Type\Atomic\TNull); + $stmt_type->addType(new TNull); } } } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php index 4d5e3add800..e28e3f06fdb 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php @@ -1,4 +1,5 @@ node_data->setType( $stmt->class, - new Type\Union([new TNamedObject($fq_class_name)]) + new Union([new TNamedObject($fq_class_name)]) ); } @@ -421,10 +425,10 @@ private static function analyzeVariableStaticPropertyFetch( foreach ($stmt_class_type->getAtomicTypes() as $class_atomic_type) { $statements_analyzer->node_data = clone $statements_analyzer->node_data; - $string_type = ($class_atomic_type instanceof Type\Atomic\TClassString + $string_type = ($class_atomic_type instanceof TClassString && $class_atomic_type->as_type !== null) ? $class_atomic_type->as_type->value - : ($class_atomic_type instanceof Type\Atomic\TLiteralString + : ($class_atomic_type instanceof TLiteralString ? $class_atomic_type->value : null); @@ -451,7 +455,7 @@ private static function analyzeVariableStaticPropertyFetch( $stmt_class->getAttributes() ); - $context->vars_in_scope['$' . $fake_var_name] = new Type\Union([$class_atomic_type]); + $context->vars_in_scope['$' . $fake_var_name] = new Union([$class_atomic_type]); $fake_instance_property = new VirtualPropertyFetch( $fake_var, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php index 77fae50dff3..3ecde22a7f9 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php @@ -1,4 +1,5 @@ getCodebase(); @@ -475,7 +480,7 @@ private static function addDataFlowToVariable( private static function taintVariable( StatementsAnalyzer $statements_analyzer, string $var_name, - Type\Union $type, + Union $type, PhpParser\Node\Expr\Variable $stmt ): void { if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph @@ -493,7 +498,7 @@ private static function taintVariable( $var_name, null, null, - Type\TaintKindGroup::ALL_INPUT + TaintKindGroup::ALL_INPUT ); $statements_analyzer->data_flow_graph->addSource($server_taint_source); @@ -517,7 +522,7 @@ public static function isSuperGlobal(string $var_id): bool ); } - public static function getGlobalType(string $var_id): Type\Union + public static function getGlobalType(string $var_id): Union { $config = Config::getInstance(); @@ -526,8 +531,8 @@ public static function getGlobalType(string $var_id): Type\Union } if ($var_id === '$argv') { - return new Type\Union([ - new Type\Atomic\TArray([Type::getInt(), Type::getString()]), + return new Union([ + new TArray([Type::getInt(), Type::getString()]), ]); } @@ -536,8 +541,8 @@ public static function getGlobalType(string $var_id): Type\Union } if ($var_id === '$http_response_header') { - return new Type\Union([ - new Type\Atomic\TList(Type::getString()) + return new Union([ + new TList(Type::getString()) ]); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php index 9689da0a732..38f9a9a3ee5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php @@ -1,4 +1,5 @@ inside_assignment = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context) === false) { - if (!$was_inside_assignment) { - $context->inside_assignment = false; - } + $context->inside_assignment = $was_inside_assignment; + return false; } - if (!$was_inside_assignment) { - $context->inside_assignment = false; - } + $context->inside_assignment = $was_inside_assignment; $stmt_var_type = $statements_analyzer->node_data->getType($stmt->var); @@ -54,7 +53,7 @@ public static function analyze( $fake_right_expr = new VirtualLNumber(1, $stmt->getAttributes()); $statements_analyzer->node_data->setType($fake_right_expr, Type::getInt()); - BinaryOp\ArithmeticOpAnalyzer::analyze( + ArithmeticOpAnalyzer::analyze( $statements_analyzer, $statements_analyzer->node_data, $stmt->var, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php index 0b158831ad6..9fd4f163ef1 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php @@ -1,4 +1,5 @@ inside_call = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) { + $context->inside_call = $was_inside_call; + return false; } - if (!$was_inside_call) { - $context->inside_call = false; - } + $context->inside_call = $was_inside_call; $stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php index f23d2b26180..e87bfe2fa8e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php @@ -1,4 +1,5 @@ inside_general_use = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) { + $context->inside_general_use = $was_inside_general_use; + return false; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php index 0f115f9d36f..40f33819210 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php @@ -1,4 +1,5 @@ node_data->setType($stmt, Type::getString($source->getCorrectlyCasedMethodId())); } else { - $statements_analyzer->node_data->setType($stmt, new Type\Union([new Type\Atomic\TCallableString])); + $statements_analyzer->node_data->setType($stmt, new Union([new TCallableString])); } } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\File || $stmt instanceof PhpParser\Node\Scalar\MagicConst\Dir ) { - $statements_analyzer->node_data->setType($stmt, new Type\Union([new Type\Atomic\TNonEmptyString()])); + $statements_analyzer->node_data->setType($stmt, new Union([new TNonEmptyString()])); } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Trait_) { if ($statements_analyzer->getSource() instanceof TraitAnalyzer) { - $statements_analyzer->node_data->setType($stmt, new Type\Union([new Type\Atomic\TNonEmptyString()])); + $statements_analyzer->node_data->setType($stmt, new Union([new TNonEmptyString()])); } else { $statements_analyzer->node_data->setType($stmt, Type::getString()); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php index 83d391ea223..139fde071d8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php @@ -1,4 +1,5 @@ getAtomicTypes(), function ($type) { - return $type instanceof Type\Atomic\TLiteralInt - || $type instanceof Type\Atomic\TLiteralString - || $type instanceof Type\Atomic\TLiteralFloat - || $type instanceof Type\Atomic\TEnumCase; + return $type instanceof TLiteralInt + || $type instanceof TLiteralString + || $type instanceof TLiteralFloat + || $type instanceof TEnumCase; } ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/NullsafeAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/NullsafeAnalyzer.php index 1fe390dfa2f..c1d49922d3f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/NullsafeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/NullsafeAnalyzer.php @@ -1,4 +1,5 @@ taints = [ - Type\TaintKind::INPUT_HTML, - Type\TaintKind::INPUT_HAS_QUOTES, - Type\TaintKind::USER_SECRET, - Type\TaintKind::SYSTEM_SECRET + TaintKind::INPUT_HTML, + TaintKind::INPUT_HAS_QUOTES, + TaintKind::USER_SECRET, + TaintKind::SYSTEM_SECRET ]; $statements_analyzer->data_flow_graph->addSink($print_param_sink); } if ($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr)) { - if (Call\ArgumentAnalyzer::verifyType( + if (ArgumentAnalyzer::verifyType( $statements_analyzer, $stmt_expr_type, Type::getString(), diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php index 4b7a7292181..5a1bb9975ed 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php @@ -1,4 +1,5 @@ isSingle() && $left->getSingleAtomic() instanceof Type\Atomic\TNonEmptyString) { - return new Type\Union([new Type\Atomic\TNonEmptyString()]); + if ($left->isSingle() && $left->getSingleAtomic() instanceof TNonEmptyString) { + return new Union([new TNonEmptyString()]); } - if ($right->isSingle() && $right->getSingleAtomic() instanceof Type\Atomic\TNonEmptyString) { - return new Type\Union([new Type\Atomic\TNonEmptyString()]); + if ($right->isSingle() && $right->getSingleAtomic() instanceof TNonEmptyString) { + return new Union([new TNonEmptyString()]); } } @@ -108,11 +122,11 @@ public static function infer( } if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Spaceship) { - return new Type\Union( + return new Union( [ - new Type\Atomic\TLiteralInt(-1), - new Type\Atomic\TLiteralInt(0), - new Type\Atomic\TLiteralInt(1) + new TLiteralInt(-1), + new TLiteralInt(0), + new TLiteralInt(1) ] ); } @@ -210,7 +224,7 @@ public static function infer( if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Dir || $stmt instanceof PhpParser\Node\Scalar\MagicConst\File ) { - return new Type\Union([new Type\Atomic\TNonEmptyString()]); + return new Union([new TNonEmptyString()]); } if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Line) { @@ -357,11 +371,11 @@ public static function infer( } foreach ($type_to_invert->getAtomicTypes() as $type_part) { - if ($type_part instanceof Type\Atomic\TLiteralInt + if ($type_part instanceof TLiteralInt && $stmt instanceof PhpParser\Node\Expr\UnaryMinus ) { $type_part->value = -$type_part->value; - } elseif ($type_part instanceof Type\Atomic\TLiteralFloat + } elseif ($type_part instanceof TLiteralFloat && $stmt instanceof PhpParser\Node\Expr\UnaryMinus ) { $type_part->value = -$type_part->value; @@ -405,7 +419,7 @@ public static function infer( } foreach ($array_type->getAtomicTypes() as $array_atomic_type) { - if ($array_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($array_atomic_type instanceof TKeyedArray) { if (isset($array_atomic_type->properties[$dim_value])) { return clone $array_atomic_type->properties[$dim_value]; } @@ -431,7 +445,7 @@ private static function inferArrayType( FileSource $file_source = null, ?array $existing_class_constants = null, ?string $fq_classlike_name = null - ): ?Type\Union { + ): ?Union { if (count($stmt->items) === 0) { return Type::getEmptyArray(); } @@ -486,21 +500,21 @@ private static function inferArrayType( && $array_creation_info->can_create_objectlike && $array_creation_info->property_types ) { - $objectlike = new Type\Atomic\TKeyedArray( + $objectlike = new TKeyedArray( $array_creation_info->property_types, $array_creation_info->class_strings ); $objectlike->sealed = true; $objectlike->is_list = $array_creation_info->all_list; - return new Type\Union([$objectlike]); + return new Union([$objectlike]); } if (!$item_key_type || !$item_value_type) { return null; } - return new Type\Union([ - new Type\Atomic\TNonEmptyArray([ + return new Union([ + new TNonEmptyArray([ $item_key_type, $item_value_type, ]), @@ -577,7 +591,7 @@ private static function handleArrayItem( $item_key_literal_type = $key_type->getSingleStringLiteral(); $item_key_value = $item_key_literal_type->value; - if ($item_key_literal_type instanceof Type\Atomic\TLiteralClassString) { + if ($item_key_literal_type instanceof TLiteralClassString) { $array_creation_info->class_strings[$item_key_value] = true; } } elseif ($key_type->isSingleIntLiteral()) { @@ -594,7 +608,7 @@ private static function handleArrayItem( } else { $item_is_list_item = true; $item_key_value = $array_creation_info->int_offset++; - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralInt($item_key_value); + $array_creation_info->item_key_atomic_types[] = new TLiteralInt($item_key_value); } $single_item_value_type = self::infer( @@ -637,10 +651,10 @@ private static function handleArrayItem( } else { $atomic_type = $dim_type->getSingleAtomic(); - if ($atomic_type instanceof Type\Atomic\TLiteralInt - || $atomic_type instanceof Type\Atomic\TLiteralString + if ($atomic_type instanceof TLiteralInt + || $atomic_type instanceof TLiteralString ) { - if ($atomic_type instanceof Type\Atomic\TLiteralClassString) { + if ($atomic_type instanceof TLiteralClassString) { $array_creation_info->class_strings[$atomic_type->value] = true; } @@ -661,17 +675,17 @@ private static function handleArrayItem( private static function handleUnpackedArray( ArrayCreationInfo $array_creation_info, - Type\Union $unpacked_array_type + Union $unpacked_array_type ): bool { foreach ($unpacked_array_type->getAtomicTypes() as $unpacked_atomic_type) { - if ($unpacked_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($unpacked_atomic_type instanceof TKeyedArray) { foreach ($unpacked_atomic_type->properties as $key => $property_value) { if (is_string($key)) { $new_offset = $key; - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralString($new_offset); + $array_creation_info->item_key_atomic_types[] = new TLiteralString($new_offset); } else { $new_offset = $array_creation_info->int_offset++; - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralInt($new_offset); + $array_creation_info->item_key_atomic_types[] = new TLiteralInt($new_offset); } $array_creation_info->item_value_atomic_types = array_merge( @@ -682,18 +696,18 @@ private static function handleUnpackedArray( $array_creation_info->array_keys[$new_offset] = true; $array_creation_info->property_types[$new_offset] = $property_value; } - } elseif ($unpacked_atomic_type instanceof Type\Atomic\TArray) { + } elseif ($unpacked_atomic_type instanceof TArray) { if ($unpacked_atomic_type->type_params[1]->isEmpty()) { continue; } $array_creation_info->can_create_objectlike = false; if ($unpacked_atomic_type->type_params[0]->hasString()) { - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TString(); + $array_creation_info->item_key_atomic_types[] = new TString(); } if ($unpacked_atomic_type->type_params[0]->hasInt()) { - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TInt(); + $array_creation_info->item_key_atomic_types[] = new TInt(); } $array_creation_info->item_value_atomic_types = array_merge( @@ -701,16 +715,16 @@ private static function handleUnpackedArray( array_values( isset($unpacked_atomic_type->type_params[1]) ? $unpacked_atomic_type->type_params[1]->getAtomicTypes() - : [new Type\Atomic\TMixed()] + : [new TMixed()] ) ); - } elseif ($unpacked_atomic_type instanceof Type\Atomic\TList) { + } elseif ($unpacked_atomic_type instanceof TList) { if ($unpacked_atomic_type->type_param->isEmpty()) { continue; } $array_creation_info->can_create_objectlike = false; - $array_creation_info->item_key_atomic_types[] = new Type\Atomic\TInt(); + $array_creation_info->item_key_atomic_types[] = new TInt(); $array_creation_info->item_value_atomic_types = array_merge( $array_creation_info->item_value_atomic_types, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php index b53065f2dc2..872c7346e1c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php @@ -1,4 +1,5 @@ node_data->getType($stmt->expr))) { - $statements_analyzer->node_data->setType($stmt, new Type\Union([new TInt, new TFloat])); + $statements_analyzer->node_data->setType($stmt, new Union([new TInt, new TFloat])); } elseif ($stmt_expr_type->isMixed()) { $statements_analyzer->node_data->setType($stmt, Type::getMixed()); } else { @@ -38,17 +43,17 @@ public static function analyze( foreach ($stmt_expr_type->getAtomicTypes() as $type_part) { if ($type_part instanceof TInt || $type_part instanceof TFloat) { - if ($type_part instanceof Type\Atomic\TLiteralInt + if ($type_part instanceof TLiteralInt && $stmt instanceof PhpParser\Node\Expr\UnaryMinus ) { $type_part->value = -$type_part->value; - } elseif ($type_part instanceof Type\Atomic\TLiteralFloat + } elseif ($type_part instanceof TLiteralFloat && $stmt instanceof PhpParser\Node\Expr\UnaryMinus ) { $type_part->value = -$type_part->value; } - if ($type_part instanceof Type\Atomic\TIntRange + if ($type_part instanceof TIntRange && $stmt instanceof PhpParser\Node\Expr\UnaryMinus ) { //we'll have to inverse min and max bound and negate any literal @@ -73,7 +78,7 @@ public static function analyze( } } - if ($type_part instanceof Type\Atomic\TPositiveInt + if ($type_part instanceof TPositiveInt && $stmt instanceof PhpParser\Node\Expr\UnaryMinus ) { $type_part = new TIntRange(null, -1); @@ -88,7 +93,7 @@ public static function analyze( } } - $statements_analyzer->node_data->setType($stmt, new Type\Union($acceptable_types)); + $statements_analyzer->node_data->setType($stmt, new Union($acceptable_types)); } self::addDataFlow( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php index ce8ff774bdc..d539c3ef467 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php @@ -1,8 +1,10 @@ type, $context->self, - $context->self ? new Type\Atomic\TNamedObject($context->self) : null, + $context->self ? new TNamedObject($context->self) : null, $statements_analyzer->getParentFQCLN() ); @@ -67,7 +71,7 @@ public static function analyze( && $var_comment->type_end && $var_comment->line_number ) { - $type_location = new CodeLocation\DocblockTypeLocation( + $type_location = new DocblockTypeLocation( $statements_analyzer, $var_comment->type_start, $var_comment->type_end, @@ -140,7 +144,7 @@ public static function analyze( $yield_type = null; foreach ($expression_type->getAtomicTypes() as $expression_atomic_type) { - if ($expression_atomic_type instanceof Type\Atomic\TNamedObject) { + if ($expression_atomic_type instanceof TNamedObject) { if (!$codebase->classlikes->classOrInterfaceExists($expression_atomic_type->value)) { continue; } @@ -148,7 +152,7 @@ public static function analyze( $classlike_storage = $codebase->classlike_storage_provider->get($expression_atomic_type->value); if ($classlike_storage->yield) { - if ($expression_atomic_type instanceof Type\Atomic\TGenericObject) { + if ($expression_atomic_type instanceof TGenericObject) { $yield_candidate_type = AtomicPropertyFetchAnalyzer::localizePropertyType( $codebase, clone $classlike_storage->yield, @@ -186,10 +190,10 @@ public static function analyze( if ($storage->return_type && !$yield_type) { foreach ($storage->return_type->getAtomicTypes() as $atomic_return_type) { - if ($atomic_return_type instanceof Type\Atomic\TNamedObject + if ($atomic_return_type instanceof TNamedObject && $atomic_return_type->value === 'Generator' ) { - if ($atomic_return_type instanceof Type\Atomic\TGenericObject) { + if ($atomic_return_type instanceof TGenericObject) { if (!$atomic_return_type->type_params[2]->isVoid()) { $statements_analyzer->node_data->setType( $stmt, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php index 72270a50013..f9c03c41335 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php @@ -1,4 +1,5 @@ inside_call = $was_inside_call; + return false; } @@ -50,14 +56,14 @@ public static function analyze( foreach ($stmt_expr_type->getAtomicTypes() as $atomic_type) { if ($yield_from_type === null) { - if ($atomic_type instanceof Type\Atomic\TGenericObject + if ($atomic_type instanceof TGenericObject && strtolower($atomic_type->value) === 'generator' && isset($atomic_type->type_params[3]) ) { $yield_from_type = clone $atomic_type->type_params[3]; - } elseif ($atomic_type instanceof Type\Atomic\TArray) { + } elseif ($atomic_type instanceof TArray) { $yield_from_type = clone $atomic_type->type_params[1]; - } elseif ($atomic_type instanceof Type\Atomic\TKeyedArray) { + } elseif ($atomic_type instanceof TKeyedArray) { $yield_from_type = $atomic_type->getGenericValueType(); } } else { diff --git a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php index 46350fb848c..0ce2af836e8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php @@ -1,4 +1,5 @@ self, $statements_analyzer, @@ -116,7 +147,7 @@ private static function handleExpression( bool $from_stmt ): bool { if ($stmt instanceof PhpParser\Node\Expr\Variable) { - return Expression\Fetch\VariableFetchAnalyzer::analyze( + return VariableFetchAnalyzer::analyze( $statements_analyzer, $stmt, $context, @@ -127,7 +158,7 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\Assign) { - $assignment_type = Expression\AssignmentAnalyzer::analyze( + $assignment_type = AssignmentAnalyzer::analyze( $statements_analyzer, $stmt->var, $stmt->expr, @@ -148,7 +179,7 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\AssignOp) { - return Expression\AssignmentAnalyzer::analyzeAssignmentOperation($statements_analyzer, $stmt, $context); + return AssignmentAnalyzer::analyzeAssignmentOperation($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\MethodCall) { @@ -160,7 +191,7 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) { - Expression\Fetch\ConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context); + ConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context); return true; } @@ -176,7 +207,7 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Scalar\MagicConst) { - Expression\MagicConstAnalyzer::analyze($statements_analyzer, $stmt, $context); + MagicConstAnalyzer::analyze($statements_analyzer, $stmt, $context); return true; } @@ -195,22 +226,22 @@ private static function handleExpression( if ($stmt instanceof PhpParser\Node\Expr\UnaryMinus || $stmt instanceof PhpParser\Node\Expr\UnaryPlus) { - return Expression\UnaryPlusMinusAnalyzer::analyze($statements_analyzer, $stmt, $context); + return UnaryPlusMinusAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\Isset_) { - Expression\IssetAnalyzer::analyze($statements_analyzer, $stmt, $context); + IssetAnalyzer::analyze($statements_analyzer, $stmt, $context); $statements_analyzer->node_data->setType($stmt, Type::getBool()); return true; } if ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch) { - return Expression\Fetch\ClassConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context); + return ClassConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\PropertyFetch) { - return Expression\Fetch\InstancePropertyFetchAnalyzer::analyze( + return InstancePropertyFetchAnalyzer::analyze( $statements_analyzer, $stmt, $context, @@ -219,7 +250,7 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\StaticPropertyFetch) { - return Expression\Fetch\StaticPropertyFetchAnalyzer::analyze( + return StaticPropertyFetchAnalyzer::analyze( $statements_analyzer, $stmt, $context @@ -227,11 +258,11 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\BitwiseNot) { - return Expression\BitwiseNotAnalyzer::analyze($statements_analyzer, $stmt, $context); + return BitwiseNotAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) { - return Expression\BinaryOpAnalyzer::analyze( + return BinaryOpAnalyzer::analyze( $statements_analyzer, $stmt, $context, @@ -245,7 +276,7 @@ private static function handleExpression( || $stmt instanceof PhpParser\Node\Expr\PreInc || $stmt instanceof PhpParser\Node\Expr\PreDec ) { - return Expression\IncDecExpressionAnalyzer::analyze($statements_analyzer, $stmt, $context); + return IncDecExpressionAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\New_) { @@ -253,11 +284,11 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\Array_) { - return Expression\ArrayAnalyzer::analyze($statements_analyzer, $stmt, $context); + return ArrayAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Scalar\Encapsed) { - return Expression\EncapsulatedStringAnalyzer::analyze($statements_analyzer, $stmt, $context); + return EncapsulatedStringAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\FuncCall) { @@ -269,15 +300,15 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\Ternary) { - return Expression\TernaryAnalyzer::analyze($statements_analyzer, $stmt, $context); + return TernaryAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\BooleanNot) { - return Expression\BooleanNotAnalyzer::analyze($statements_analyzer, $stmt, $context); + return BooleanNotAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\Empty_) { - Expression\EmptyAnalyzer::analyze($statements_analyzer, $stmt, $context); + EmptyAnalyzer::analyze($statements_analyzer, $stmt, $context); return true; } @@ -289,7 +320,7 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch) { - return Expression\Fetch\ArrayFetchAnalyzer::analyze( + return ArrayFetchAnalyzer::analyze( $statements_analyzer, $stmt, $context @@ -297,32 +328,32 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\Cast) { - return Expression\CastAnalyzer::analyze($statements_analyzer, $stmt, $context); + return CastAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\Clone_) { - return Expression\CloneAnalyzer::analyze($statements_analyzer, $stmt, $context); + return CloneAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\Instanceof_) { - return Expression\InstanceofAnalyzer::analyze($statements_analyzer, $stmt, $context); + return InstanceofAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\Exit_) { - return Expression\ExitAnalyzer::analyze($statements_analyzer, $stmt, $context); + return ExitAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\Include_) { - return Expression\IncludeAnalyzer::analyze($statements_analyzer, $stmt, $context, $global_context); + return IncludeAnalyzer::analyze($statements_analyzer, $stmt, $context, $global_context); } if ($stmt instanceof PhpParser\Node\Expr\Eval_) { - Expression\EvalAnalyzer::analyze($statements_analyzer, $stmt, $context); + EvalAnalyzer::analyze($statements_analyzer, $stmt, $context); return true; } if ($stmt instanceof PhpParser\Node\Expr\AssignRef) { - return Expression\AssignmentAnalyzer::analyzeAssignmentRef($statements_analyzer, $stmt, $context); + return AssignmentAnalyzer::analyzeAssignmentRef($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\ErrorSuppress) { @@ -421,7 +452,9 @@ private static function handleExpression( if ($stmt instanceof PhpParser\Node\Expr\Print_) { $was_inside_call = $context->inside_call; $context->inside_call = true; - if (Expression\PrintAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) { + if (PrintAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) { + $context->inside_call = $was_inside_call; + return false; } $context->inside_call = $was_inside_call; @@ -430,18 +463,18 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\Yield_) { - return Expression\YieldAnalyzer::analyze($statements_analyzer, $stmt, $context); + return YieldAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\YieldFrom) { - return Expression\YieldFromAnalyzer::analyze($statements_analyzer, $stmt, $context); + return YieldFromAnalyzer::analyze($statements_analyzer, $stmt, $context); } $php_major_version = $statements_analyzer->getCodebase()->php_major_version; $php_minor_version = $statements_analyzer->getCodebase()->php_minor_version; if ($stmt instanceof PhpParser\Node\Expr\Match_ && $php_major_version >= 8) { - return Expression\MatchAnalyzer::analyze($statements_analyzer, $stmt, $context); + return MatchAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\Throw_ && $php_major_version >= 8) { @@ -452,7 +485,7 @@ private static function handleExpression( || $stmt instanceof PhpParser\Node\Expr\NullsafeMethodCall) && $php_major_version >= 8 ) { - return Expression\NullsafeAnalyzer::analyze($statements_analyzer, $stmt, $context); + return NullsafeAnalyzer::analyze($statements_analyzer, $stmt, $context); } if ($stmt instanceof PhpParser\Node\Expr\Error) { diff --git a/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php index e0b2dd30b9a..c716bc2bfbc 100644 --- a/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php @@ -1,4 +1,5 @@ type_end && $var_comment->line_number ) { - $type_location = new CodeLocation\DocblockTypeLocation( + $type_location = new DocblockTypeLocation( $statements_analyzer, $var_comment->type_start, $var_comment->type_end, @@ -422,7 +429,7 @@ public static function analyze( } foreach ($local_return_type->getAtomicTypes() as $local_type_part) { - if ($local_type_part instanceof Type\Atomic\TClassString + if ($local_type_part instanceof TClassString && $stmt->expr instanceof PhpParser\Node\Scalar\String_ ) { if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName( @@ -437,13 +444,13 @@ public static function analyze( ) { return; } - } elseif ($local_type_part instanceof Type\Atomic\TArray + } elseif ($local_type_part instanceof TArray && $stmt->expr instanceof PhpParser\Node\Expr\Array_ ) { $value_param = $local_type_part->type_params[1]; foreach ($value_param->getAtomicTypes() as $local_array_type_part) { - if ($local_array_type_part instanceof Type\Atomic\TClassString) { + if ($local_array_type_part instanceof TClassString) { foreach ($stmt->expr->items as $item) { if ($item && $item->value instanceof PhpParser\Node\Scalar\String_) { if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName( @@ -531,7 +538,7 @@ private static function handleTaints( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Stmt\Return_ $stmt, string $cased_method_id, - Type\Union $inferred_type, + Union $inferred_type, FunctionLikeStorage $storage ): void { if (!$statements_analyzer->data_flow_graph instanceof TaintFlowGraph @@ -604,7 +611,7 @@ private static function potentiallyInferTypesOnClosureFromParentReturnType( return; } - /** @var Type\Atomic\TClosure|Type\Atomic\TCallable $parent_callable_return_type */ + /** @var TClosure|TCallable $parent_callable_return_type */ $parent_callable_return_type = $parent_fn_storage->return_type->getSingleAtomic(); if ($parent_callable_return_type->params === null && $parent_callable_return_type->return_type === null) { @@ -635,9 +642,9 @@ private static function potentiallyInferTypesOnClosureFromParentReturnType( */ private static function inferInnerClosureTypeFromParent( Codebase $codebase, - ?Type\Union $return_type, - ?Type\Union $parent_return_type - ): ?Type\Union { + ?Union $return_type, + ?Union $parent_return_type + ): ?Union { if (!$parent_return_type) { return $return_type; } diff --git a/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php index 5f1aff503d5..6133afd4509 100644 --- a/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php @@ -1,8 +1,10 @@ type_end && $var_comment->line_number ) { - $type_location = new CodeLocation\DocblockTypeLocation( + $type_location = new DocblockTypeLocation( $statements_analyzer, $var_comment->type_start, $var_comment->type_end, diff --git a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php index 9eb5bc3dc5d..665a1c40d4d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php @@ -1,4 +1,5 @@ vars_in_scope[$root_var_id]; foreach ($root_type->getAtomicTypes() as $atomic_root_type) { - if ($atomic_root_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_root_type instanceof TKeyedArray) { if ($var->dim instanceof PhpParser\Node\Scalar\String_ || $var->dim instanceof PhpParser\Node\Scalar\LNumber ) { @@ -64,18 +74,18 @@ public static function analyze( if (!$atomic_root_type->properties) { if ($atomic_root_type->previous_value_type) { $root_type->addType( - new Type\Atomic\TArray([ + new TArray([ $atomic_root_type->previous_key_type ? clone $atomic_root_type->previous_key_type - : new Type\Union([new Type\Atomic\TArrayKey]), + : new Union([new TArrayKey]), clone $atomic_root_type->previous_value_type, ]) ); } else { $root_type->addType( - new Type\Atomic\TArray([ - new Type\Union([new Type\Atomic\TEmpty]), - new Type\Union([new Type\Atomic\TEmpty]), + new TArray([ + new Union([new TEmpty]), + new Union([new TEmpty]), ]) ); } @@ -94,17 +104,17 @@ public static function analyze( $atomic_root_type->is_list = false; } - } elseif ($atomic_root_type instanceof Type\Atomic\TNonEmptyArray) { + } elseif ($atomic_root_type instanceof TNonEmptyArray) { $root_type->addType( - new Type\Atomic\TArray($atomic_root_type->type_params) + new TArray($atomic_root_type->type_params) ); - } elseif ($atomic_root_type instanceof Type\Atomic\TNonEmptyMixed) { + } elseif ($atomic_root_type instanceof TNonEmptyMixed) { $root_type->addType( - new Type\Atomic\TMixed() + new TMixed() ); - } elseif ($atomic_root_type instanceof Type\Atomic\TList) { + } elseif ($atomic_root_type instanceof TList) { $root_type->addType( - new Type\Atomic\TArray([ + new TArray([ Type::getInt(), $atomic_root_type->type_param ]) diff --git a/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php b/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php index 5d25d735f07..30c01b4e28f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php +++ b/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php @@ -1,4 +1,5 @@ node_data, $stmt->expr->getArgs()[1]->value, @@ -536,7 +544,7 @@ private static function analyzeStatement( } elseif ($stmt instanceof PhpParser\Node\Stmt\Const_) { ConstFetchAnalyzer::analyzeConstAssignment($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Unset_) { - Statements\UnsetAnalyzer::analyze($statements_analyzer, $stmt, $context); + UnsetAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Return_) { ReturnAnalyzer::analyze($statements_analyzer, $stmt, $context); $context->has_returned = true; @@ -546,13 +554,13 @@ private static function analyzeStatement( } elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) { SwitchAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Break_) { - Statements\BreakAnalyzer::analyze($statements_analyzer, $stmt, $context); + BreakAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Continue_) { - Statements\ContinueAnalyzer::analyze($statements_analyzer, $stmt, $context); + ContinueAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Static_) { - Statements\StaticAnalyzer::analyze($statements_analyzer, $stmt, $context); + StaticAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Echo_) { - if (Statements\EchoAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) { + if (EchoAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) { return false; } } elseif ($stmt instanceof PhpParser\Node\Stmt\Function_) { @@ -571,7 +579,7 @@ private static function analyzeStatement( } elseif ($stmt instanceof PhpParser\Node\Stmt\InlineHTML) { // do nothing } elseif ($stmt instanceof PhpParser\Node\Stmt\Global_) { - Statements\GlobalAnalyzer::analyze($statements_analyzer, $stmt, $context, $global_context); + GlobalAnalyzer::analyze($statements_analyzer, $stmt, $context, $global_context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Property) { InstancePropertyAssignmentAnalyzer::analyzeStatement($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\ClassConst) { @@ -727,7 +735,7 @@ public function checkUnreferencedVars(array $stmts, Context $context): void $project_analyzer = $this->getProjectAnalyzer(); - $unused_var_remover = new Statements\UnusedAssignmentRemover(); + $unused_var_remover = new UnusedAssignmentRemover(); if ($this->data_flow_graph instanceof VariableUseGraph && $codebase->config->limit_method_complexity diff --git a/src/Psalm/Internal/Analyzer/TraitAnalyzer.php b/src/Psalm/Internal/Analyzer/TraitAnalyzer.php index a8b208d6fe2..c59fc8c0b9d 100644 --- a/src/Psalm/Internal/Analyzer/TraitAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/TraitAnalyzer.php @@ -1,4 +1,5 @@ $new_types - * @param array $existing_types + * @param array $new_types + * @param array $existing_types * - * @return array + * @return array */ public static function combineKeyedTypes(array $new_types, array $existing_types): array { diff --git a/src/Psalm/Internal/Clause.php b/src/Psalm/Internal/Clause.php index 4eb32980923..5497f579b9b 100644 --- a/src/Psalm/Internal/Clause.php +++ b/src/Psalm/Internal/Clause.php @@ -1,4 +1,5 @@ file_reference_provider->getFileMaps(); @@ -1149,7 +1153,7 @@ private static function configureProjectAnalyzer( private static function generateStubs( array $options, - Provider\Providers $providers, + Providers $providers, ProjectAnalyzer $project_analyzer ): void { if (isset($options['generate-stubs']) && is_string($options['generate-stubs'])) { diff --git a/src/Psalm/Internal/Codebase/Analyzer.php b/src/Psalm/Internal/Codebase/Analyzer.php index 6ad8333bca3..36c927131d3 100644 --- a/src/Psalm/Internal/Codebase/Analyzer.php +++ b/src/Psalm/Internal/Codebase/Analyzer.php @@ -1,4 +1,5 @@ config, $process_file_paths, function (): void { $project_analyzer = ProjectAnalyzer::getInstance(); diff --git a/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php b/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php index 9c553f5b5ca..d3863440c07 100644 --- a/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php +++ b/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php @@ -1,10 +1,11 @@ |null + * @return list|null */ public function resolve(string $class_name, string $constant_pattern): ?array { diff --git a/src/Psalm/Internal/Codebase/ClassLikes.php b/src/Psalm/Internal/Codebase/ClassLikes.php index a9260050668..909bae82d08 100644 --- a/src/Psalm/Internal/Codebase/ClassLikes.php +++ b/src/Psalm/Internal/Codebase/ClassLikes.php @@ -1,4 +1,5 @@ classlike_storage_provider->has($class_name)) { @@ -1661,7 +1664,7 @@ public function getClassConstantType( } if ($constant_storage->unresolved_node) { - $constant_storage->type = new Type\Union([ConstantTypeResolver::resolve( + $constant_storage->type = new Union([ConstantTypeResolver::resolve( $this, $constant_storage->unresolved_node, $statements_analyzer, @@ -1671,7 +1674,7 @@ public function getClassConstantType( return $constant_storage->type; } elseif (isset($storage->enum_cases[$constant_name])) { - return new Type\Union([new Type\Atomic\TEnumCase($storage->name, $constant_name)]); + return new Union([new TEnumCase($storage->name, $constant_name)]); } return null; } @@ -2022,7 +2025,7 @@ private function findPossibleMethodParamTypes(ClassLikeStorage $classlike_storag } if ($method_storage->params[$offset]->default_type) { - if ($method_storage->params[$offset]->default_type instanceof Type\Union) { + if ($method_storage->params[$offset]->default_type instanceof Union) { $default_type = clone $method_storage->params[$offset]->default_type; } else { $default_type_atomic = ConstantTypeResolver::resolve( @@ -2031,7 +2034,7 @@ private function findPossibleMethodParamTypes(ClassLikeStorage $classlike_storag null ); - $default_type = new Type\Union([$default_type_atomic]); + $default_type = new Union([$default_type_atomic]); } $possible_type = Type::combineUnionTypes( diff --git a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php index 209c2923916..c27cc523f7c 100644 --- a/src/Psalm/Internal/Codebase/ConstantTypeResolver.php +++ b/src/Psalm/Internal/Codebase/ConstantTypeResolver.php @@ -1,12 +1,42 @@ value); } - if ($c instanceof UnresolvedConstant\UnresolvedBinaryOp) { + if ($c instanceof UnresolvedBinaryOp) { $left = self::resolve( $classlikes, $c->left, @@ -50,73 +80,73 @@ public static function resolve( $visited_constant_ids + [$c_id => true] ); - if ($left instanceof Type\Atomic\TMixed || $right instanceof Type\Atomic\TMixed) { - return new Type\Atomic\TMixed; + if ($left instanceof TMixed || $right instanceof TMixed) { + return new TMixed; } - if ($c instanceof UnresolvedConstant\UnresolvedConcatOp) { - if (($left instanceof Type\Atomic\TLiteralString - || $left instanceof Type\Atomic\TLiteralFloat - || $left instanceof Type\Atomic\TLiteralInt) - && ($right instanceof Type\Atomic\TLiteralString - || $right instanceof Type\Atomic\TLiteralFloat - || $right instanceof Type\Atomic\TLiteralInt) + if ($c instanceof UnresolvedConcatOp) { + if (($left instanceof TLiteralString + || $left instanceof TLiteralFloat + || $left instanceof TLiteralInt) + && ($right instanceof TLiteralString + || $right instanceof TLiteralFloat + || $right instanceof TLiteralInt) ) { - return new Type\Atomic\TLiteralString($left->value . $right->value); + return new TLiteralString($left->value . $right->value); } - return new Type\Atomic\TString(); + return new TString(); } - if ($c instanceof UnresolvedConstant\UnresolvedAdditionOp - || $c instanceof UnresolvedConstant\UnresolvedSubtractionOp - || $c instanceof UnresolvedConstant\UnresolvedDivisionOp - || $c instanceof UnresolvedConstant\UnresolvedMultiplicationOp - || $c instanceof UnresolvedConstant\UnresolvedBitwiseOr - || $c instanceof UnresolvedConstant\UnresolvedBitwiseXor - || $c instanceof UnresolvedConstant\UnresolvedBitwiseAnd + if ($c instanceof UnresolvedAdditionOp + || $c instanceof UnresolvedSubtractionOp + || $c instanceof UnresolvedDivisionOp + || $c instanceof UnresolvedMultiplicationOp + || $c instanceof UnresolvedBitwiseOr + || $c instanceof UnresolvedBitwiseXor + || $c instanceof UnresolvedBitwiseAnd ) { - if (($left instanceof Type\Atomic\TLiteralFloat || $left instanceof Type\Atomic\TLiteralInt) - && ($right instanceof Type\Atomic\TLiteralFloat || $right instanceof Type\Atomic\TLiteralInt) + if (($left instanceof TLiteralFloat || $left instanceof TLiteralInt) + && ($right instanceof TLiteralFloat || $right instanceof TLiteralInt) ) { - if ($c instanceof UnresolvedConstant\UnresolvedAdditionOp) { + if ($c instanceof UnresolvedAdditionOp) { return self::getLiteralTypeFromScalarValue($left->value + $right->value); } - if ($c instanceof UnresolvedConstant\UnresolvedSubtractionOp) { + if ($c instanceof UnresolvedSubtractionOp) { return self::getLiteralTypeFromScalarValue($left->value - $right->value); } - if ($c instanceof UnresolvedConstant\UnresolvedDivisionOp) { + if ($c instanceof UnresolvedDivisionOp) { return self::getLiteralTypeFromScalarValue($left->value / $right->value); } - if ($c instanceof UnresolvedConstant\UnresolvedBitwiseOr) { + if ($c instanceof UnresolvedBitwiseOr) { return self::getLiteralTypeFromScalarValue($left->value | $right->value); } - if ($c instanceof UnresolvedConstant\UnresolvedBitwiseXor) { + if ($c instanceof UnresolvedBitwiseXor) { return self::getLiteralTypeFromScalarValue($left->value ^ $right->value); } - if ($c instanceof UnresolvedConstant\UnresolvedBitwiseAnd) { + if ($c instanceof UnresolvedBitwiseAnd) { return self::getLiteralTypeFromScalarValue($left->value & $right->value); } return self::getLiteralTypeFromScalarValue($left->value * $right->value); } - if ($left instanceof Type\Atomic\TKeyedArray && $right instanceof Type\Atomic\TKeyedArray) { - return new Type\Atomic\TKeyedArray($left->properties + $right->properties); + if ($left instanceof TKeyedArray && $right instanceof TKeyedArray) { + return new TKeyedArray($left->properties + $right->properties); } - return new Type\Atomic\TMixed; + return new TMixed; } - return new Type\Atomic\TMixed; + return new TMixed; } - if ($c instanceof UnresolvedConstant\UnresolvedTernary) { + if ($c instanceof UnresolvedTernary) { $cond = self::resolve( $classlikes, $c->cond, @@ -136,32 +166,32 @@ public static function resolve( $visited_constant_ids + [$c_id => true] ); - if ($cond instanceof Type\Atomic\TLiteralFloat - || $cond instanceof Type\Atomic\TLiteralInt - || $cond instanceof Type\Atomic\TLiteralString + if ($cond instanceof TLiteralFloat + || $cond instanceof TLiteralInt + || $cond instanceof TLiteralString ) { if ($cond->value) { return $if ?? $cond; } - } elseif ($cond instanceof Type\Atomic\TFalse || $cond instanceof Type\Atomic\TNull) { + } elseif ($cond instanceof TFalse || $cond instanceof TNull) { return $else; - } elseif ($cond instanceof Type\Atomic\TTrue) { + } elseif ($cond instanceof TTrue) { return $if ?? $cond; } } - if ($c instanceof UnresolvedConstant\ArrayValue) { + if ($c instanceof ArrayValue) { $properties = []; $auto_key = 0; if (!$c->entries) { - return new Type\Atomic\TArray([Type::getEmpty(), Type::getEmpty()]); + return new TArray([Type::getEmpty(), Type::getEmpty()]); } $is_list = true; foreach ($c->entries as $entry) { - if ($entry instanceof UnresolvedConstant\ArraySpread) { + if ($entry instanceof ArraySpread) { $spread_array = self::resolve( $classlikes, $entry->array, @@ -169,12 +199,12 @@ public static function resolve( $visited_constant_ids + [$c_id => true] ); - if ($spread_array instanceof Type\Atomic\TArray && $spread_array->type_params[1]->isEmpty()) { + if ($spread_array instanceof TArray && $spread_array->type_params[1]->isEmpty()) { continue; } - if (!$spread_array instanceof Type\Atomic\TKeyedArray) { - return new Type\Atomic\TArray([Type::getArrayKey(), Type::getMixed()]); + if (!$spread_array instanceof TKeyedArray) { + return new TArray([Type::getArrayKey(), Type::getMixed()]); } foreach ($spread_array->properties as $spread_array_type) { @@ -191,29 +221,29 @@ public static function resolve( $visited_constant_ids + [$c_id => true] ); - if (!$key_type instanceof Type\Atomic\TLiteralInt + if (!$key_type instanceof TLiteralInt || $key_type->value !== $auto_key ) { $is_list = false; } } else { - $key_type = new Type\Atomic\TLiteralInt($auto_key); + $key_type = new TLiteralInt($auto_key); } - if ($key_type instanceof Type\Atomic\TLiteralInt - || $key_type instanceof Type\Atomic\TLiteralString + if ($key_type instanceof TLiteralInt + || $key_type instanceof TLiteralString ) { $key_value = $key_type->value; - if ($key_type instanceof Type\Atomic\TLiteralInt) { + if ($key_type instanceof TLiteralInt) { $auto_key = $key_type->value + 1; } elseif (ctype_digit($key_type->value)) { $auto_key = ((int) $key_type->value) + 1; } } else { - return new Type\Atomic\TArray([Type::getArrayKey(), Type::getMixed()]); + return new TArray([Type::getArrayKey(), Type::getMixed()]); } - $value_type = new Type\Union([self::resolve( + $value_type = new Union([self::resolve( $classlikes, $entry->value, $statements_analyzer, @@ -224,12 +254,12 @@ public static function resolve( } if (empty($properties)) { - $resolved_type = new Type\Atomic\TArray([ - new Type\Union([new Type\Atomic\TEmpty()]), - new Type\Union([new Type\Atomic\TEmpty()]), + $resolved_type = new TArray([ + new Union([new TEmpty()]), + new Union([new TEmpty()]), ]); } else { - $resolved_type = new Type\Atomic\TKeyedArray($properties); + $resolved_type = new TKeyedArray($properties); $resolved_type->is_list = $is_list; $resolved_type->sealed = true; @@ -238,9 +268,9 @@ public static function resolve( return $resolved_type; } - if ($c instanceof UnresolvedConstant\ClassConstant) { + if ($c instanceof ClassConstant) { if ($c->name === 'class') { - return new Type\Atomic\TLiteralClassString($c->fqcln); + return new TLiteralClassString($c->fqcln); } $found_type = $classlikes->getClassConstantType( @@ -256,7 +286,7 @@ public static function resolve( } } - if ($c instanceof UnresolvedConstant\ArrayOffsetFetch) { + if ($c instanceof ArrayOffsetFetch) { $var_type = self::resolve( $classlikes, $c->array, @@ -271,9 +301,9 @@ public static function resolve( $visited_constant_ids + [$c_id => true] ); - if ($var_type instanceof Type\Atomic\TKeyedArray - && ($offset_type instanceof Type\Atomic\TLiteralInt - || $offset_type instanceof Type\Atomic\TLiteralString) + if ($var_type instanceof TKeyedArray + && ($offset_type instanceof TLiteralInt + || $offset_type instanceof TLiteralString) ) { $union = $var_type->properties[$offset_type->value] ?? null; @@ -283,7 +313,7 @@ public static function resolve( } } - if ($c instanceof UnresolvedConstant\Constant) { + if ($c instanceof Constant) { if ($statements_analyzer) { $found_type = ConstFetchAnalyzer::getConstType( $statements_analyzer, @@ -298,34 +328,34 @@ public static function resolve( } } - return new Type\Atomic\TMixed; + return new TMixed; } /** * @param string|int|float|bool|null $value */ - private static function getLiteralTypeFromScalarValue($value): Type\Atomic + private static function getLiteralTypeFromScalarValue($value): Atomic { if (is_string($value)) { - return new Type\Atomic\TLiteralString($value); + return new TLiteralString($value); } if (is_int($value)) { - return new Type\Atomic\TLiteralInt($value); + return new TLiteralInt($value); } if (is_float($value)) { - return new Type\Atomic\TLiteralFloat($value); + return new TLiteralFloat($value); } if ($value === false) { - return new Type\Atomic\TFalse; + return new TFalse; } if ($value === true) { - return new Type\Atomic\TTrue; + return new TTrue; } - return new Type\Atomic\TNull; + return new TNull; } } diff --git a/src/Psalm/Internal/Codebase/Functions.php b/src/Psalm/Internal/Codebase/Functions.php index 63845cbcad5..a31bebd6954 100644 --- a/src/Psalm/Internal/Codebase/Functions.php +++ b/src/Psalm/Internal/Codebase/Functions.php @@ -1,4 +1,5 @@ >> + * @var array>> */ private static $taint_sink_map = []; @@ -161,13 +166,13 @@ public static function getMatchingCallableFromCallMapOptions( if ($arg_type->hasArray()) { /** * @psalm-suppress PossiblyUndefinedStringArrayOffset - * @var Type\Atomic\TArray|Type\Atomic\TKeyedArray|Type\Atomic\TList + * @var TArray|TKeyedArray|TList */ $array_atomic_type = $arg_type->getAtomicTypes()['array']; - if ($array_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($array_atomic_type instanceof TKeyedArray) { $arg_type = $array_atomic_type->getGenericValueType(); - } elseif ($array_atomic_type instanceof Type\Atomic\TList) { + } elseif ($array_atomic_type instanceof TList) { $arg_type = $array_atomic_type->type_param; } else { $arg_type = $array_atomic_type->type_params[1]; @@ -369,7 +374,7 @@ public static function getCallMap(): array } /** - * @var array>> + * @var array>> */ $taint_map = require(dirname(__DIR__, 4) . '/dictionaries/InternalTaintSinkMap.php'); diff --git a/src/Psalm/Internal/Codebase/Methods.php b/src/Psalm/Internal/Codebase/Methods.php index 94afca13281..8dadb1929ed 100644 --- a/src/Psalm/Internal/Codebase/Methods.php +++ b/src/Psalm/Internal/Codebase/Methods.php @@ -1,4 +1,5 @@ inside_call = false; - } + $context->inside_call = $was_inside_call; } $matching_callable = InternalCallMapHandler::getMatchingCallableFromCallMapOptions( @@ -467,7 +480,7 @@ public function getMethodParams( && $overridden_storage->params[$i]->has_docblock_type ) { $params[$i] = clone $param; - /** @var Type\Union $params[$i]->type */ + /** @var Union $params[$i]->type */ $params[$i]->type = clone $overridden_storage->params[$i]->type; if ($source) { @@ -483,7 +496,7 @@ public function getMethodParams( if ($params[$i]->signature_type && $params[$i]->signature_type->isNullable() ) { - $params[$i]->type->addType(new Type\Atomic\TNull); + $params[$i]->type->addType(new TNull); } $params[$i]->type_location = $overridden_storage->params[$i]->type_location; @@ -498,10 +511,10 @@ public function getMethodParams( public static function localizeType( Codebase $codebase, - Type\Union $type, + Union $type, string $appearing_fq_class_name, string $base_fq_class_name - ): Type\Union { + ): Union { $class_storage = $codebase->classlike_storage_provider->get($appearing_fq_class_name); $extends = $class_storage->template_extended_params; @@ -512,7 +525,7 @@ public static function localizeType( $type = clone $type; foreach ($type->getAtomicTypes() as $key => $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TTemplateParam + if ($atomic_type instanceof TTemplateParam && ($atomic_type->defining_class === $base_fq_class_name || isset($extends[$atomic_type->defining_class])) ) { @@ -530,14 +543,14 @@ public static function localizeType( } } - if ($atomic_type instanceof Type\Atomic\TTemplateParamClass) { + if ($atomic_type instanceof TTemplateParamClass) { if ($atomic_type->defining_class === $base_fq_class_name) { if (isset($extends[$base_fq_class_name][$atomic_type->param_name])) { $extended_param = $extends[$base_fq_class_name][$atomic_type->param_name]; $types = array_values($extended_param->getAtomicTypes()); - if (count($types) === 1 && $types[0] instanceof Type\Atomic\TNamedObject) { + if (count($types) === 1 && $types[0] instanceof TNamedObject) { $atomic_type->as_type = $types[0]; } else { $atomic_type->as_type = null; @@ -546,9 +559,9 @@ public static function localizeType( } } - if ($atomic_type instanceof Type\Atomic\TArray - || $atomic_type instanceof Type\Atomic\TIterable - || $atomic_type instanceof Type\Atomic\TGenericObject + if ($atomic_type instanceof TArray + || $atomic_type instanceof TIterable + || $atomic_type instanceof TGenericObject ) { foreach ($atomic_type->type_params as &$type_param) { $type_param = self::localizeType( @@ -560,7 +573,7 @@ public static function localizeType( } } - if ($atomic_type instanceof Type\Atomic\TList) { + if ($atomic_type instanceof TList) { $atomic_type->type_param = self::localizeType( $codebase, $atomic_type->type_param, @@ -569,7 +582,7 @@ public static function localizeType( ); } - if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_type instanceof TKeyedArray) { foreach ($atomic_type->properties as &$property_type) { $property_type = self::localizeType( $codebase, @@ -580,8 +593,8 @@ public static function localizeType( } } - if ($atomic_type instanceof Type\Atomic\TCallable - || $atomic_type instanceof Type\Atomic\TClosure + if ($atomic_type instanceof TCallable + || $atomic_type instanceof TClosure ) { if ($atomic_type->params) { foreach ($atomic_type->params as $param) { @@ -613,11 +626,11 @@ public static function localizeType( } /** - * @param array> $extends - * @return list + * @param array> $extends + * @return list */ public static function getExtendedTemplatedTypes( - Type\Atomic\TTemplateParam $atomic_type, + TTemplateParam $atomic_type, array $extends ): array { $extra_added_types = []; @@ -626,7 +639,7 @@ public static function getExtendedTemplatedTypes( $extended_param = clone $extends[$atomic_type->defining_class][$atomic_type->param_name]; foreach ($extended_param->getAtomicTypes() as $extended_atomic_type) { - if ($extended_atomic_type instanceof Type\Atomic\TTemplateParam) { + if ($extended_atomic_type instanceof TTemplateParam) { $extra_added_types = array_merge( $extra_added_types, self::getExtendedTemplatedTypes( @@ -665,7 +678,7 @@ public function getMethodReturnType( ?string &$self_class, ?SourceAnalyzer $source_analyzer = null, ?array $args = null - ): ?Type\Union { + ): ?Union { $original_fq_class_name = $method_id->fq_class_name; $original_method_name = $method_id->method_name; @@ -714,13 +727,13 @@ public function getMethodReturnType( $types = []; foreach ($original_class_storage->enum_cases as $case_name => $_) { - $types[] = new Type\Union([new Type\Atomic\TEnumCase($original_fq_class_name, $case_name)]); + $types[] = new Union([new TEnumCase($original_fq_class_name, $case_name)]); } - $list = new Type\Atomic\TKeyedArray($types); + $list = new TKeyedArray($types); $list->is_list = true; $list->sealed = true; - return new Type\Union([$list]); + return new Union([$list]); } } @@ -743,14 +756,14 @@ public function getMethodReturnType( Type::getString($case_storage->value), $first_arg_type )) { - $types[] = new Type\Atomic\TEnumCase($original_fq_class_name, $case_name); + $types[] = new TEnumCase($original_fq_class_name, $case_name); } } if ($types) { if ($original_method_name === 'tryfrom') { - $types[] = new Type\Atomic\TNull(); + $types[] = new TNull(); } - return new Type\Union($types); + return new Union($types); } return $original_method_name === 'tryfrom' ? Type::getNull() : Type::getNever(); } @@ -767,19 +780,19 @@ public function getMethodReturnType( && $first_arg_type->isSingle() ) { foreach ($first_arg_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TCallable - || $atomic_type instanceof Type\Atomic\TClosure + if ($atomic_type instanceof TCallable + || $atomic_type instanceof TClosure ) { $callable_type = clone $atomic_type; - return new Type\Union([new Type\Atomic\TClosure( + return new Union([new TClosure( 'Closure', $callable_type->params, $callable_type->return_type )]); } - if ($atomic_type instanceof Type\Atomic\TNamedObject + if ($atomic_type instanceof TNamedObject && $this->methodExists( new MethodIdentifier($atomic_type->value, '__invoke') ) @@ -788,7 +801,7 @@ public function getMethodReturnType( new MethodIdentifier($atomic_type->value, '__invoke') ); - return new Type\Union([new Type\Atomic\TClosure( + return new Union([new TClosure( 'Closure', $invokable_storage->params, $invokable_storage->return_type diff --git a/src/Psalm/Internal/Codebase/Populator.php b/src/Psalm/Internal/Codebase/Populator.php index 3117e627700..c293367f7f6 100644 --- a/src/Psalm/Internal/Codebase/Populator.php +++ b/src/Psalm/Internal/Codebase/Populator.php @@ -1,4 +1,5 @@ getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TTemplateParam) { + if ($atomic_type instanceof TTemplateParam) { $referenced_type = $storage->template_extended_params[$atomic_type->defining_class][$atomic_type->param_name] ?? null; if ($referenced_type) { foreach ($referenced_type->getAtomicTypes() as $atomic_referenced_type) { - if (!$atomic_referenced_type instanceof Type\Atomic\TTemplateParam) { + if (!$atomic_referenced_type instanceof TTemplateParam) { $extended_types[] = $atomic_referenced_type; } else { $extended_types[] = $atomic_type; @@ -472,7 +479,7 @@ private static function extendType( } } - return new Type\Union($extended_types); + return new Union($extended_types); } private function populateDataFromParentClass( @@ -984,7 +991,7 @@ private function populateFileStorage(FileStorage $storage, array $dependent_file $storage->populated = true; } - private function convertPhpStormGenericToPsalmGeneric(Type\Union $candidate, bool $is_property = false): void + private function convertPhpStormGenericToPsalmGeneric(Union $candidate, bool $is_property = false): void { if (!$candidate->from_docblock) { //never convert a type that comes from a signature @@ -1000,8 +1007,8 @@ private function convertPhpStormGenericToPsalmGeneric(Type\Union $candidate, boo try { foreach ($atomic_types as $type_key => $type) { - if ($type instanceof Type\Atomic\TIterable - || ($type instanceof Type\Atomic\TNamedObject + if ($type instanceof TIterable + || ($type instanceof TNamedObject && (!$type->from_docblock || $is_property) && ( strtolower($type->value) === 'traversable' @@ -1017,7 +1024,7 @@ private function convertPhpStormGenericToPsalmGeneric(Type\Union $candidate, boo ) { $iterator_name = $type->value; $iterator_key = $type_key; - } elseif ($type instanceof Type\Atomic\TArray) { + } elseif ($type instanceof TArray) { $generic_params = $type->type_params; } } @@ -1027,13 +1034,13 @@ private function convertPhpStormGenericToPsalmGeneric(Type\Union $candidate, boo if ($iterator_name && $iterator_key && $generic_params) { if ($iterator_name === 'iterable') { - $generic_iterator = new Type\Atomic\TIterable($generic_params); + $generic_iterator = new TIterable($generic_params); } else { if (strtolower($iterator_name) === 'generator') { $generic_params[] = Type::getMixed(); $generic_params[] = Type::getMixed(); } - $generic_iterator = new Type\Atomic\TGenericObject($iterator_name, $generic_params); + $generic_iterator = new TGenericObject($iterator_name, $generic_params); } $candidate->removeType('array'); diff --git a/src/Psalm/Internal/Codebase/Properties.php b/src/Psalm/Internal/Codebase/Properties.php index 221aa2aa134..2a0137a5db6 100644 --- a/src/Psalm/Internal/Codebase/Properties.php +++ b/src/Psalm/Internal/Codebase/Properties.php @@ -1,4 +1,5 @@ config, $process_file_paths, function (): void { $this->progress->debug('Initialising forked process for scanning' . PHP_EOL); diff --git a/src/Psalm/Internal/Diff/AstDiffer.php b/src/Psalm/Internal/Diff/AstDiffer.php index 8f96624d0a2..b0588459670 100644 --- a/src/Psalm/Internal/Diff/AstDiffer.php +++ b/src/Psalm/Internal/Diff/AstDiffer.php @@ -1,5 +1,7 @@ > + * @var list> */ private $after_method_checks = []; - /** @var list> */ + /** @var list> */ private $legacy_after_method_checks = []; /** @@ -29,10 +71,10 @@ class EventDispatcher * * Allows influencing the return type and adding of modifications. * - * @var list> + * @var list> */ public $after_function_checks = []; - /** @var list> */ + /** @var list> */ public $legacy_after_function_checks = []; /** @@ -42,122 +84,122 @@ class EventDispatcher * * Cannot change the call or influence its return type * - * @var list> + * @var list> */ public $after_every_function_checks = []; - /** @var list> */ + /** @var list> */ public $legacy_after_every_function_checks = []; /** * Static methods to be called after expression checks have completed * - * @var list> + * @var list> */ public $after_expression_checks = []; - /** @var list> */ + /** @var list> */ public $legacy_after_expression_checks = []; /** * Static methods to be called after statement checks have completed * - * @var list> + * @var list> */ public $after_statement_checks = []; - /** @var list> */ + /** @var list> */ public $legacy_after_statement_checks = []; /** * Static methods to be called after method checks have completed * - * @var list> + * @var list> */ public $string_interpreters = []; - /** @var list> */ + /** @var list> */ public $legacy_string_interpreters = []; /** * Static methods to be called after classlike exists checks have completed * - * @var list> + * @var list> */ public $after_classlike_exists_checks = []; - /** @var list> */ + /** @var list> */ public $legacy_after_classlike_exists_checks = []; /** * Static methods to be called after classlike checks have completed * - * @var list> + * @var list> */ public $after_classlike_checks = []; - /** @var list> */ + /** @var list> */ public $legacy_after_classlike_checks = []; /** * Static methods to be called after classlikes have been scanned * - * @var list> + * @var list> */ private $after_visit_classlikes = []; - /** @var list> */ + /** @var list> */ private $legacy_after_visit_classlikes = []; /** * Static methods to be called after codebase has been populated * - * @var list> + * @var list> */ public $after_codebase_populated = []; - /** @var list> */ + /** @var list> */ public $legacy_after_codebase_populated = []; /** * Static methods to be called after codebase has been populated * - * @var list> + * @var list> */ public $after_analysis = []; - /** @var list> */ + /** @var list> */ public $legacy_after_analysis = []; /** * Static methods to be called after a file has been analyzed * - * @var list> + * @var list> */ public $after_file_checks = []; - /** @var list> */ + /** @var list> */ public $legacy_after_file_checks = []; /** * Static methods to be called before a file is analyzed * - * @var list> + * @var list> */ public $before_file_checks = []; - /** @var list> */ + /** @var list> */ public $legacy_before_file_checks = []; /** * Static methods to be called after functionlike checks have completed * - * @var list> + * @var list> */ public $after_functionlike_checks = []; - /** @var list> */ + /** @var list> */ public $legacy_after_functionlike_checks = []; /** * Static methods to be called to see if taints should be added * - * @var list> + * @var list> */ public $add_taints_checks = []; /** * Static methods to be called to see if taints should be removed * - * @var list> + * @var list> */ public $remove_taints_checks = []; @@ -166,95 +208,95 @@ class EventDispatcher */ public function registerClass(string $class): void { - if (is_subclass_of($class, Hook\AfterMethodCallAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyAfterMethodCallAnalysisInterface::class)) { $this->legacy_after_method_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterMethodCallAnalysisInterface::class)) { + } elseif (is_subclass_of($class, AfterMethodCallAnalysisInterface::class)) { $this->after_method_checks[] = $class; } - if (is_subclass_of($class, Hook\AfterFunctionCallAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyAfterFunctionCallAnalysisInterface::class)) { $this->legacy_after_function_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterFunctionCallAnalysisInterface::class)) { + } elseif (is_subclass_of($class, AfterFunctionCallAnalysisInterface::class)) { $this->after_function_checks[] = $class; } - if (is_subclass_of($class, Hook\AfterEveryFunctionCallAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyAfterEveryFunctionCallAnalysisInterface::class)) { $this->legacy_after_every_function_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterEveryFunctionCallAnalysisInterface::class)) { + } elseif (is_subclass_of($class, AfterEveryFunctionCallAnalysisInterface::class)) { $this->after_every_function_checks[] = $class; } - if (is_subclass_of($class, Hook\AfterExpressionAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyAfterExpressionAnalysisInterface::class)) { $this->legacy_after_expression_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterExpressionAnalysisInterface::class)) { + } elseif (is_subclass_of($class, AfterExpressionAnalysisInterface::class)) { $this->after_expression_checks[] = $class; } - if (is_subclass_of($class, Hook\AfterStatementAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyAfterStatementAnalysisInterface::class)) { $this->legacy_after_statement_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterStatementAnalysisInterface::class)) { + } elseif (is_subclass_of($class, AfterStatementAnalysisInterface::class)) { $this->after_statement_checks[] = $class; } - if (is_subclass_of($class, Hook\StringInterpreterInterface::class)) { + if (is_subclass_of($class, LegacyStringInterpreterInterface::class)) { $this->legacy_string_interpreters[] = $class; - } elseif (is_subclass_of($class, EventHandler\StringInterpreterInterface::class)) { + } elseif (is_subclass_of($class, StringInterpreterInterface::class)) { $this->string_interpreters[] = $class; } - if (is_subclass_of($class, Hook\AfterClassLikeExistenceCheckInterface::class)) { + if (is_subclass_of($class, LegacyAfterClassLikeExistenceCheckInterface::class)) { $this->legacy_after_classlike_exists_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterClassLikeExistenceCheckInterface::class)) { + } elseif (is_subclass_of($class, AfterClassLikeExistenceCheckInterface::class)) { $this->after_classlike_exists_checks[] = $class; } - if (is_subclass_of($class, Hook\AfterClassLikeAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyAfterClassLikeAnalysisInterface::class)) { $this->legacy_after_classlike_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterClassLikeAnalysisInterface::class)) { + } elseif (is_subclass_of($class, AfterClassLikeAnalysisInterface::class)) { $this->after_classlike_checks[] = $class; } - if (is_subclass_of($class, Hook\AfterClassLikeVisitInterface::class)) { + if (is_subclass_of($class, LegacyAfterClassLikeVisitInterface::class)) { $this->legacy_after_visit_classlikes[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterClassLikeVisitInterface::class)) { + } elseif (is_subclass_of($class, AfterClassLikeVisitInterface::class)) { $this->after_visit_classlikes[] = $class; } - if (is_subclass_of($class, Hook\AfterCodebasePopulatedInterface::class)) { + if (is_subclass_of($class, LegacyAfterCodebasePopulatedInterface::class)) { $this->legacy_after_codebase_populated[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterCodebasePopulatedInterface::class)) { + } elseif (is_subclass_of($class, AfterCodebasePopulatedInterface::class)) { $this->after_codebase_populated[] = $class; } - if (is_subclass_of($class, Hook\AfterAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyAfterAnalysisInterface::class)) { $this->legacy_after_analysis[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterAnalysisInterface::class)) { + } elseif (is_subclass_of($class, AfterAnalysisInterface::class)) { $this->after_analysis[] = $class; } - if (is_subclass_of($class, Hook\AfterFileAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyAfterFileAnalysisInterface::class)) { $this->legacy_after_file_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterFileAnalysisInterface::class)) { + } elseif (is_subclass_of($class, AfterFileAnalysisInterface::class)) { $this->after_file_checks[] = $class; } - if (is_subclass_of($class, Hook\BeforeFileAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyBeforeFileAnalysisInterface::class)) { $this->legacy_before_file_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\BeforeFileAnalysisInterface::class)) { + } elseif (is_subclass_of($class, BeforeFileAnalysisInterface::class)) { $this->before_file_checks[] = $class; } - if (is_subclass_of($class, Hook\AfterFunctionLikeAnalysisInterface::class)) { + if (is_subclass_of($class, LegacyAfterFunctionLikeAnalysisInterface::class)) { $this->legacy_after_functionlike_checks[] = $class; - } elseif (is_subclass_of($class, EventHandler\AfterFunctionLikeAnalysisInterface::class)) { + } elseif (is_subclass_of($class, AfterFunctionLikeAnalysisInterface::class)) { $this->after_functionlike_checks[] = $class; } - if (is_subclass_of($class, EventHandler\AddTaintsInterface::class)) { + if (is_subclass_of($class, AddTaintsInterface::class)) { $this->add_taints_checks[] = $class; } - if (is_subclass_of($class, EventHandler\RemoveTaintsInterface::class)) { + if (is_subclass_of($class, RemoveTaintsInterface::class)) { $this->remove_taints_checks[] = $class; } } @@ -264,7 +306,7 @@ public function hasAfterMethodCallAnalysisHandlers(): bool return count($this->after_method_checks) || count($this->legacy_after_method_checks); } - public function dispatchAfterMethodCallAnalysis(Event\AfterMethodCallAnalysisEvent $event): void + public function dispatchAfterMethodCallAnalysis(AfterMethodCallAnalysisEvent $event): void { foreach ($this->after_method_checks as $handler) { $handler::afterMethodCallAnalysis($event); @@ -289,7 +331,7 @@ public function dispatchAfterMethodCallAnalysis(Event\AfterMethodCallAnalysisEve } } - public function dispatchAfterFunctionCallAnalysis(Event\AfterFunctionCallAnalysisEvent $event): void + public function dispatchAfterFunctionCallAnalysis(AfterFunctionCallAnalysisEvent $event): void { foreach ($this->after_function_checks as $handler) { $handler::afterFunctionCallAnalysis($event); @@ -310,7 +352,7 @@ public function dispatchAfterFunctionCallAnalysis(Event\AfterFunctionCallAnalysi } } - public function dispatchAfterEveryFunctionCallAnalysis(Event\AfterEveryFunctionCallAnalysisEvent $event): void + public function dispatchAfterEveryFunctionCallAnalysis(AfterEveryFunctionCallAnalysisEvent $event): void { foreach ($this->after_every_function_checks as $handler) { $handler::afterEveryFunctionCallAnalysis($event); @@ -327,7 +369,7 @@ public function dispatchAfterEveryFunctionCallAnalysis(Event\AfterEveryFunctionC } } - public function dispatchAfterExpressionAnalysis(Event\AfterExpressionAnalysisEvent $event): ?bool + public function dispatchAfterExpressionAnalysis(AfterExpressionAnalysisEvent $event): ?bool { foreach ($this->after_expression_checks as $handler) { if ($handler::afterExpressionAnalysis($event) === false) { @@ -352,7 +394,7 @@ public function dispatchAfterExpressionAnalysis(Event\AfterExpressionAnalysisEve return null; } - public function dispatchAfterStatementAnalysis(Event\AfterStatementAnalysisEvent $event): ?bool + public function dispatchAfterStatementAnalysis(AfterStatementAnalysisEvent $event): ?bool { foreach ($this->after_statement_checks as $handler) { if ($handler::afterStatementAnalysis($event) === false) { @@ -377,7 +419,7 @@ public function dispatchAfterStatementAnalysis(Event\AfterStatementAnalysisEvent return null; } - public function dispatchStringInterpreter(Event\StringInterpreterEvent $event): ?TLiteralString + public function dispatchStringInterpreter(StringInterpreterEvent $event): ?TLiteralString { foreach ($this->string_interpreters as $handler) { if ($type = $handler::getTypeFromValue($event)) { @@ -394,7 +436,7 @@ public function dispatchStringInterpreter(Event\StringInterpreterEvent $event): return null; } - public function dispatchAfterClassLikeExistenceCheck(Event\AfterClassLikeExistenceCheckEvent $event): void + public function dispatchAfterClassLikeExistenceCheck(AfterClassLikeExistenceCheckEvent $event): void { foreach ($this->after_classlike_exists_checks as $handler) { $handler::afterClassLikeExistenceCheck($event); @@ -413,7 +455,7 @@ public function dispatchAfterClassLikeExistenceCheck(Event\AfterClassLikeExisten } } - public function dispatchAfterClassLikeAnalysis(Event\AfterClassLikeAnalysisEvent $event): ?bool + public function dispatchAfterClassLikeAnalysis(AfterClassLikeAnalysisEvent $event): ?bool { foreach ($this->after_classlike_checks as $handler) { if ($handler::afterStatementAnalysis($event) === false) { @@ -443,7 +485,7 @@ public function hasAfterClassLikeVisitHandlers(): bool return count($this->after_visit_classlikes) || count($this->legacy_after_visit_classlikes); } - public function dispatchAfterClassLikeVisit(Event\AfterClassLikeVisitEvent $event): void + public function dispatchAfterClassLikeVisit(AfterClassLikeVisitEvent $event): void { foreach ($this->after_visit_classlikes as $handler) { $handler::afterClassLikeVisit($event); @@ -462,7 +504,7 @@ public function dispatchAfterClassLikeVisit(Event\AfterClassLikeVisitEvent $even } } - public function dispatchAfterCodebasePopulated(Event\AfterCodebasePopulatedEvent $event): void + public function dispatchAfterCodebasePopulated(AfterCodebasePopulatedEvent $event): void { foreach ($this->after_codebase_populated as $handler) { $handler::afterCodebasePopulated($event); @@ -475,7 +517,7 @@ public function dispatchAfterCodebasePopulated(Event\AfterCodebasePopulatedEvent } } - public function dispatchAfterAnalysis(Event\AfterAnalysisEvent $event): void + public function dispatchAfterAnalysis(AfterAnalysisEvent $event): void { foreach ($this->after_analysis as $handler) { $handler::afterAnalysis($event); @@ -492,7 +534,7 @@ public function dispatchAfterAnalysis(Event\AfterAnalysisEvent $event): void } } - public function dispatchAfterFileAnalysis(Event\AfterFileAnalysisEvent $event): void + public function dispatchAfterFileAnalysis(AfterFileAnalysisEvent $event): void { foreach ($this->after_file_checks as $handler) { $handler::afterAnalyzeFile($event); @@ -508,7 +550,7 @@ public function dispatchAfterFileAnalysis(Event\AfterFileAnalysisEvent $event): } } - public function dispatchBeforeFileAnalysis(Event\BeforeFileAnalysisEvent $event): void + public function dispatchBeforeFileAnalysis(BeforeFileAnalysisEvent $event): void { foreach ($this->before_file_checks as $handler) { $handler::beforeAnalyzeFile($event); @@ -524,7 +566,7 @@ public function dispatchBeforeFileAnalysis(Event\BeforeFileAnalysisEvent $event) } } - public function dispatchAfterFunctionLikeAnalysis(Event\AfterFunctionLikeAnalysisEvent $event): ?bool + public function dispatchAfterFunctionLikeAnalysis(AfterFunctionLikeAnalysisEvent $event): ?bool { foreach ($this->after_functionlike_checks as $handler) { if ($handler::afterStatementAnalysis($event) === false) { @@ -552,7 +594,7 @@ public function dispatchAfterFunctionLikeAnalysis(Event\AfterFunctionLikeAnalysi /** * @return list */ - public function dispatchAddTaints(Event\AddRemoveTaintsEvent $event): array + public function dispatchAddTaints(AddRemoveTaintsEvent $event): array { $added_taints = []; @@ -566,7 +608,7 @@ public function dispatchAddTaints(Event\AddRemoveTaintsEvent $event): array /** * @return list */ - public function dispatchRemoveTaints(Event\AddRemoveTaintsEvent $event): array + public function dispatchRemoveTaints(AddRemoveTaintsEvent $event): array { $removed_taints = []; diff --git a/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php b/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php index eba0b71f9c8..b59445b01ab 100644 --- a/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php +++ b/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php @@ -1,4 +1,5 @@ getSelectionBounds(); diff --git a/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php b/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php index dfad6e08179..147ba4e7ccf 100644 --- a/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php +++ b/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php @@ -1,4 +1,5 @@ > $process_task_data_iterator * An array of task data items to be divided up among the * workers. The size of this is the number of forked processes. @@ -104,6 +112,7 @@ class Pool * @psalm-suppress MixedAssignment */ public function __construct( + Config $config, array $process_task_data_iterator, Closure $startup_closure, Closure $task_closure, @@ -112,6 +121,7 @@ public function __construct( ) { $pool_size = count($process_task_data_iterator); $this->task_done_closure = $task_done_closure; + $this->config = $config; assert( $pool_size > 1, @@ -201,7 +211,12 @@ public function __construct( $task_result = $task_closure($i, $task_data); $task_done_message = new ForkTaskDoneMessage($task_result); - $serialized_message = $task_done_buffer . base64_encode(serialize($task_done_message)) . "\n"; + if ($this->config->use_igbinary) { + $encoded_message = base64_encode(igbinary_serialize($task_done_message)); + } else { + $encoded_message = base64_encode(serialize($task_done_message)); + } + $serialized_message = $task_done_buffer . $encoded_message . "\n"; if (strlen($serialized_message) > 200) { $bytes_written = @fwrite($write_stream, $serialized_message); @@ -233,7 +248,12 @@ public function __construct( ); } - $serialized_message = $task_done_buffer . base64_encode(serialize($process_done_message)) . "\n"; + if ($this->config->use_igbinary) { + $encoded_message = base64_encode(igbinary_serialize($process_done_message)); + } else { + $encoded_message = base64_encode(serialize($process_done_message)); + } + $serialized_message = $task_done_buffer . $encoded_message . "\n"; $bytes_to_write = strlen($serialized_message); $bytes_written = 0; @@ -356,7 +376,11 @@ private function readResultsFromChildren(): array $content[(int)$file] = array_pop($serialized_messages); foreach ($serialized_messages as $serialized_message) { - $message = unserialize(base64_decode($serialized_message, true)); + if ($this->config->use_igbinary) { + $message = igbinary_unserialize(base64_decode($serialized_message, true)); + } else { + $message = unserialize(base64_decode($serialized_message, true)); + } if ($message instanceof ForkProcessDoneMessage) { $terminationMessages[] = $message->data; diff --git a/src/Psalm/Internal/Fork/PsalmRestarter.php b/src/Psalm/Internal/Fork/PsalmRestarter.php index eabc30f530d..66d25ac0062 100644 --- a/src/Psalm/Internal/Fork/PsalmRestarter.php +++ b/src/Psalm/Internal/Fork/PsalmRestarter.php @@ -1,4 +1,5 @@ protocolWriter->write( new Message( - new AdvancedJsonRpc\Request($id, $method, (object) $params) + new Request($id, $method, (object) $params) ) ); @@ -70,12 +75,12 @@ function (Message $msg) use ($id, $deferred, &$listener): void { * @psalm-suppress MixedArgument */ if ($msg->body - && AdvancedJsonRpc\Response::isResponse($msg->body) + && Response::isResponse($msg->body) && $msg->body->id === $id ) { // Received a response $this->protocolReader->removeListener('message', $listener); - if (AdvancedJsonRpc\SuccessResponse::isSuccessResponse($msg->body)) { + if (SuccessResponse::isSuccessResponse($msg->body)) { $deferred->resolve($msg->body->result); } else { $deferred->fail($msg->body->error); @@ -99,7 +104,7 @@ public function notify(string $method, $params): void { $this->protocolWriter->write( new Message( - new AdvancedJsonRpc\Notification($method, (object)$params) + new Notification($method, (object)$params) ) ); } diff --git a/src/Psalm/Internal/LanguageServer/EmitterInterface.php b/src/Psalm/Internal/LanguageServer/EmitterInterface.php index 860528b930a..9f8f80ba0fc 100644 --- a/src/Psalm/Internal/LanguageServer/EmitterInterface.php +++ b/src/Psalm/Internal/LanguageServer/EmitterInterface.php @@ -1,5 +1,7 @@ handler = new ClientHandler($reader, $writer); $mapper = new JsonMapper; - $this->textDocument = new Client\TextDocument($this->handler, $mapper); + $this->textDocument = new ClientTextDocument($this->handler, $mapper); } /** diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index 8af4a65378e..f6525aacea6 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -1,8 +1,16 @@ body)) { + if (Response::isResponse($msg->body)) { return; } @@ -144,14 +152,14 @@ function (Message $msg): Generator { $dispatched = $this->dispatch($msg->body); /** @psalm-suppress MixedAssignment */ $result = yield $dispatched; - } catch (AdvancedJsonRpc\Error $e) { + } catch (Error $e) { // If a ResponseError is thrown, send it back in the Response $error = $e; } catch (Throwable $e) { // If an unexpected error occurred, send back an INTERNAL_ERROR error response - $error = new AdvancedJsonRpc\Error( + $error = new Error( (string) $e, - AdvancedJsonRpc\ErrorCode::INTERNAL_ERROR, + ErrorCode::INTERNAL_ERROR, null, $e ); @@ -162,11 +170,11 @@ function (Message $msg): Generator { * @psalm-suppress UndefinedPropertyFetch * @psalm-suppress MixedArgument */ - if (AdvancedJsonRpc\Request::isRequest($msg->body)) { + if (Request::isRequest($msg->body)) { if ($error !== null) { - $responseBody = new AdvancedJsonRpc\ErrorResponse($msg->body->id, $error); + $responseBody = new ErrorResponse($msg->body->id, $error); } else { - $responseBody = new AdvancedJsonRpc\SuccessResponse($msg->body->id, $result); + $responseBody = new SuccessResponse($msg->body->id, $result); } yield $this->protocolWriter->write(new Message($responseBody)); } @@ -227,7 +235,7 @@ function () { $codebase->config->visitStubFiles($codebase); if ($this->textDocument === null) { - $this->textDocument = new TextDocument( + $this->textDocument = new ServerTextDocument( $this, $codebase, $this->project_analyzer->onchange_line_limit @@ -235,7 +243,7 @@ function () { } if ($this->workspace === null) { - $this->workspace = new Workspace( + $this->workspace = new ServerWorkspace( $this, $codebase, $this->project_analyzer->onchange_line_limit diff --git a/src/Psalm/Internal/LanguageServer/Message.php b/src/Psalm/Internal/LanguageServer/Message.php index bf23422d032..be3362530de 100644 --- a/src/Psalm/Internal/LanguageServer/Message.php +++ b/src/Psalm/Internal/LanguageServer/Message.php @@ -1,5 +1,7 @@ tags['psalm-yield']) - ) { + if (isset($parsed_docblock->tags['psalm-yield'])) { $yield = reset($parsed_docblock->tags['psalm-yield']); $info->yield = trim(preg_replace('@^[ \t]*\*@m', '', $yield)); @@ -355,12 +358,12 @@ public static function parse( throw new DocblockParseException($method_entry . ' is not a valid method'); } - if (!$method_tree instanceof ParseTree\MethodWithReturnTypeTree - && !$method_tree instanceof ParseTree\MethodTree) { + if (!$method_tree instanceof MethodWithReturnTypeTree + && !$method_tree instanceof MethodTree) { throw new DocblockParseException($method_entry . ' is not a valid method'); } - if ($method_tree instanceof ParseTree\MethodWithReturnTypeTree) { + if ($method_tree instanceof MethodWithReturnTypeTree) { if (!$has_return) { $docblock_lines[] = '@return ' . TypeParser::getTypeFromTree( $method_tree->children[1], @@ -371,14 +374,14 @@ public static function parse( $method_tree = $method_tree->children[0]; } - if (!$method_tree instanceof ParseTree\MethodTree) { + if (!$method_tree instanceof MethodTree) { throw new DocblockParseException($method_entry . ' is not a valid method'); } $args = []; foreach ($method_tree->children as $method_tree_child) { - if (!$method_tree_child instanceof ParseTree\MethodParamTree) { + if (!$method_tree_child instanceof MethodParamTree) { throw new DocblockParseException($method_entry . ' is not a valid method'); } @@ -437,7 +440,9 @@ public static function parse( /** @var Doc */ $node_doc_comment = $node->getDocComment(); - $statements[0]->stmts[0]->setAttribute('startLine', $node_doc_comment->getStartLine()); + $method_offset = self::getMethodOffset($comment, $method_entry); + + $statements[0]->stmts[0]->setAttribute('startLine', $node_doc_comment->getStartLine() + $method_offset); $statements[0]->stmts[0]->setAttribute('startFilePos', $node_doc_comment->getStartFilePos()); $statements[0]->stmts[0]->setAttribute('endFilePos', $node->getAttribute('startFilePos')); @@ -545,4 +550,19 @@ protected static function addMagicPropertyToInfo( } } } + + private static function getMethodOffset(Doc $comment, string $method_entry): int + { + $lines = explode("\n", $comment->getText()); + $method_offset = 0; + + foreach ($lines as $i => $line) { + if (strpos($line, $method_entry) !== false) { + $method_offset = $i; + break; + } + } + + return $method_offset; + } } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php index a9e09456b40..718f2d25a24 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php @@ -1,4 +1,5 @@ + * @var array */ private $classlike_type_aliases = []; /** - * @var array> + * @var array> */ public $class_template_types = []; @@ -660,11 +671,11 @@ function (array $l, array $r): int { if ($mixin_type->isSingle()) { $mixin_type = $mixin_type->getSingleAtomic(); - if ($mixin_type instanceof Type\Atomic\TNamedObject) { + if ($mixin_type instanceof TNamedObject) { $storage->namedMixins[] = $mixin_type; } - if ($mixin_type instanceof Type\Atomic\TTemplateParam) { + if ($mixin_type instanceof TTemplateParam) { $storage->templatedMixins[] = $mixin_type; } } @@ -777,7 +788,7 @@ public function finish(PhpParser\Node\Stmt\ClassLike $node): ClassLikeStorage } $converted_aliases = array_map( - function (TypeAlias\InlineTypeAlias $t): ?TypeAlias\ClassTypeAlias { + function (InlineTypeAlias $t): ?ClassTypeAlias { try { $union = TypeParser::parseTokens( $t->replacement_tokens, @@ -788,7 +799,7 @@ function (TypeAlias\InlineTypeAlias $t): ?TypeAlias\ClassTypeAlias { $union->setFromDocblock(); - return new TypeAlias\ClassTypeAlias( + return new ClassTypeAlias( array_values($union->getAtomicTypes()) ); } catch (Exception $e) { @@ -947,7 +958,7 @@ private function extendTemplatedType( ); foreach ($extended_union_type->getAtomicTypes() as $atomic_type) { - if (!$atomic_type instanceof Type\Atomic\TGenericObject) { + if (!$atomic_type instanceof TGenericObject) { $storage->docblock_issues[] = new InvalidDocblock( '@template-extends has invalid class ' . $atomic_type->getId(), new CodeLocation($this->file_scanner, $node, null, true) @@ -1033,7 +1044,7 @@ private function implementTemplatedType( ); foreach ($implemented_union_type->getAtomicTypes() as $atomic_type) { - if (!$atomic_type instanceof Type\Atomic\TGenericObject) { + if (!$atomic_type instanceof TGenericObject) { $storage->docblock_issues[] = new InvalidDocblock( '@template-implements has invalid class ' . $atomic_type->getId(), new CodeLocation($this->file_scanner, $node, null, true) @@ -1119,7 +1130,7 @@ private function useTemplatedType( ); foreach ($used_union_type->getAtomicTypes() as $atomic_type) { - if (!$atomic_type instanceof Type\Atomic\TGenericObject) { + if (!$atomic_type instanceof TGenericObject) { $storage->docblock_issues[] = new InvalidDocblock( '@template-use has invalid class ' . $atomic_type->getId(), new CodeLocation($this->file_scanner, $node, null, true) @@ -1249,7 +1260,7 @@ private function visitClassConstDeclaration( if ($const_type && $const->value instanceof Concat && $const_type->isSingle() - && get_class($const_type->getSingleAtomic()) === Type\Atomic\TString::class + && get_class($const_type->getSingleAtomic()) === TString::class ) { // Prefer unresolved type over inferred string from concat, so that it can later be resolved to literal. $const_type = null; @@ -1335,10 +1346,38 @@ private function visitEnumDeclaration( $case_location = new CodeLocation($this->file_scanner, $stmt); if (!isset($storage->enum_cases[$stmt->name->name])) { - $storage->enum_cases[$stmt->name->name] = new EnumCaseStorage( + $case = new EnumCaseStorage( $enum_value, $case_location ); + + $attrs = $this->getAttributeStorageFromStatement( + $this->codebase, + $this->file_scanner, + $this->file_storage, + $this->aliases, + $stmt, + $this->storage->name ?? null + ); + + foreach ($attrs as $attribute) { + if ($attribute->fq_class_name === 'Psalm\\Deprecated' + || $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated' + ) { + $case->deprecated = true; + break; + } + } + + $comment = $stmt->getDocComment(); + if ($comment) { + $comments = DocComment::parsePreservingLength($comment); + + if (isset($comments->tags['deprecated'])) { + $case->deprecated = true; + } + } + $storage->enum_cases[$stmt->name->name] = $case; } else { if (IssueBuffer::accepts( new DuplicateEnumCase( @@ -1351,6 +1390,34 @@ private function visitEnumDeclaration( } } + /** + * @param PhpParser\Node\Stmt\Property|PhpParser\Node\Stmt\EnumCase $stmt + * @return list + */ + private function getAttributeStorageFromStatement( + Codebase $codebase, + FileScanner $file_scanner, + FileStorage $file_storage, + Aliases $aliases, + PhpParser\Node\Stmt $stmt, + ?string $fq_classlike_name + ): array { + $storages = []; + foreach ($stmt->attrGroups as $attr_group) { + foreach ($attr_group->attrs as $attr) { + $storages[] = AttributeResolver::resolve( + $codebase, + $file_scanner, + $file_storage, + $aliases, + $attr, + $fq_classlike_name + ); + } + } + return $storages; + } + private function visitPropertyDeclaration( PhpParser\Node\Stmt\Property $stmt, Config $config, @@ -1477,7 +1544,7 @@ private function visitPropertyDeclaration( && $var_comment->type_end && $var_comment->line_number ) { - $doc_var_location = new CodeLocation\DocblockTypeLocation( + $doc_var_location = new DocblockTypeLocation( $this->file_scanner, $var_comment->type_start, $var_comment->type_end, @@ -1518,7 +1585,7 @@ private function visitPropertyDeclaration( if ($property_storage->signature_type->isNullable() && !$property_storage->type->isNullable() ) { - $property_storage->type->addType(new Type\Atomic\TNull()); + $property_storage->type->addType(new TNull()); } } @@ -1546,33 +1613,31 @@ private function visitPropertyDeclaration( $storage->inheritable_property_ids[$property->name->name] = $property_id; } - foreach ($stmt->attrGroups as $attr_group) { - foreach ($attr_group->attrs as $attr) { - $attribute = AttributeResolver::resolve( - $this->codebase, - $this->file_scanner, - $this->file_storage, - $this->aliases, - $attr, - $this->storage->name ?? null - ); - - if ($attribute->fq_class_name === 'Psalm\\Deprecated' - || $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated' - ) { - $property_storage->deprecated = true; - } + $attrs = $this->getAttributeStorageFromStatement( + $this->codebase, + $this->file_scanner, + $this->file_storage, + $this->aliases, + $stmt, + $this->storage->name ?? null + ); - if ($attribute->fq_class_name === 'Psalm\\Internal' && !$property_storage->internal) { - $property_storage->internal = NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name); - } + foreach ($attrs as $attribute) { + if ($attribute->fq_class_name === 'Psalm\\Deprecated' + || $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated' + ) { + $property_storage->deprecated = true; + } - if ($attribute->fq_class_name === 'Psalm\\Readonly') { - $property_storage->readonly = true; - } + if ($attribute->fq_class_name === 'Psalm\\Internal' && !$property_storage->internal) { + $property_storage->internal = NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name); + } - $property_storage->attributes[] = $attribute; + if ($attribute->fq_class_name === 'Psalm\\Readonly') { + $property_storage->readonly = true; } + + $property_storage->attributes[] = $attribute; } } } @@ -1581,11 +1646,11 @@ private function visitPropertyDeclaration( * @param ClassLikeDocblockComment $comment * @param string $fq_classlike_name * - * @return array + * @return array */ private function getImportedTypeAliases(ClassLikeDocblockComment $comment, string $fq_classlike_name): array { - /** @var array $results */ + /** @var array $results */ $results = []; foreach ($comment->imported_types as $import_type_entry) { @@ -1653,7 +1718,7 @@ private function getImportedTypeAliases(ClassLikeDocblockComment $comment, strin $this->file_storage->referenced_classlikes[strtolower($declaring_fq_classlike_name)] = $declaring_fq_classlike_name; - $results[$as_alias_name] = new TypeAlias\LinkableTypeAlias( + $results[$as_alias_name] = new LinkableTypeAlias( $declaring_fq_classlike_name, $type_alias_name, $import_type_entry['line_number'], @@ -1668,7 +1733,7 @@ private function getImportedTypeAliases(ClassLikeDocblockComment $comment, strin /** * @param array $type_aliases * - * @return array + * @return array * * @throws DocblockParseException if there was a problem parsing the docblock */ @@ -1701,7 +1766,7 @@ public static function getTypeAliasesFromComment( * @param array $type_alias_comment_lines * @param array $type_aliases * - * @return array + * @return array * * @throws DocblockParseException if there was a problem parsing the docblock */ @@ -1769,7 +1834,7 @@ private static function getTypeAliasesFromCommentLines( throw new DocblockParseException($type_string . ' is not a valid type'); } - $type_alias_tokens[$type_alias] = new TypeAlias\InlineTypeAlias($type_tokens); + $type_alias_tokens[$type_alias] = new InlineTypeAlias($type_tokens); } return $type_alias_tokens; diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php index aaf75b75bff..3cf9d02950d 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php @@ -1,4 +1,5 @@ name->parts[0]); if ($part0_lc === 'false') { - return new UnresolvedConstant\ScalarValue(false); + return new ScalarValue(false); } if ($part0_lc === 'true') { - return new UnresolvedConstant\ScalarValue(true); + return new ScalarValue(true); } if ($part0_lc === 'null') { - return new UnresolvedConstant\ScalarValue(null); + return new ScalarValue(null); } if ($part0_lc === '__namespace__') { - return new UnresolvedConstant\ScalarValue($aliases->namespace); + return new ScalarValue($aliases->namespace); } - return new UnresolvedConstant\Constant( + return new Constant( implode('\\', $stmt->name->parts), $stmt->name instanceof PhpParser\Node\Name\FullyQualified ); } if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Namespace_) { - return new UnresolvedConstant\ScalarValue($aliases->namespace); + return new ScalarValue($aliases->namespace); } if ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch && $stmt->dim) { @@ -160,7 +176,7 @@ public static function getUnresolvedClassConstExpr( ); if ($left && $right) { - return new UnresolvedConstant\ArrayOffsetFetch($left, $right); + return new ArrayOffsetFetch($left, $right); } } @@ -185,7 +201,7 @@ public static function getUnresolvedClassConstExpr( } } - return new UnresolvedConstant\ClassConstant($const_fq_class_name, $stmt->name->name); + return new ClassConstant($const_fq_class_name, $stmt->name->name); } return null; @@ -195,7 +211,7 @@ public static function getUnresolvedClassConstExpr( || $stmt instanceof PhpParser\Node\Scalar\LNumber || $stmt instanceof PhpParser\Node\Scalar\DNumber ) { - return new UnresolvedConstant\ScalarValue($stmt->value); + return new ScalarValue($stmt->value); } if ($stmt instanceof PhpParser\Node\Expr\UnaryPlus) { @@ -210,8 +226,8 @@ public static function getUnresolvedClassConstExpr( return null; } - return new UnresolvedConstant\UnresolvedAdditionOp( - new UnresolvedConstant\ScalarValue(0), + return new UnresolvedAdditionOp( + new ScalarValue(0), $right ); } @@ -228,8 +244,8 @@ public static function getUnresolvedClassConstExpr( return null; } - return new UnresolvedConstant\UnresolvedSubtractionOp( - new UnresolvedConstant\ScalarValue(0), + return new UnresolvedSubtractionOp( + new ScalarValue(0), $right ); } @@ -269,13 +285,13 @@ public static function getUnresolvedClassConstExpr( } if ($item->unpack) { - $items[] = new UnresolvedConstant\ArraySpread($item_value_type); + $items[] = new ArraySpread($item_value_type); } else { - $items[] = new UnresolvedConstant\KeyValuePair($item_key_type, $item_value_type); + $items[] = new KeyValuePair($item_key_type, $item_value_type); } } - return new UnresolvedConstant\ArrayValue($items); + return new ArrayValue($items); } return null; diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php index f38865008bb..6e88891a20a 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php @@ -1,4 +1,5 @@ > $existing_function_template_types + * @param array> $existing_function_template_types * @param array $type_aliases */ public static function addDocblockInfo( @@ -153,7 +164,7 @@ public static function addDocblockInfo( $storage->suppressed_issues = $docblock_info->suppressed_issues; foreach ($docblock_info->throws as [$throw, $offset, $line]) { - $throw_location = new CodeLocation\DocblockTypeLocation( + $throw_location = new DocblockTypeLocation( $file_scanner, $offset, $offset + strlen($throw), @@ -398,13 +409,13 @@ function (FunctionLikeParameter $p): bool { } /** - * @param array> $template_types + * @param array> $template_types * @param array|null $type_aliases - * @param array> $function_template_types + * @param array> $function_template_types * * @return array{ * array, - * array> + * array> * } */ private static function getConditionalSanitizedTypeTokens( @@ -452,8 +463,8 @@ private static function getConditionalSanitizedTypeTokens( $param_type_mapping[$token_body] = $template_name; - $param_storage->type = new Type\Union([ - new Type\Atomic\TTemplateParam( + $param_storage->type = new Union([ + new TTemplateParam( $template_name, $template_as_type, $template_function_id @@ -520,8 +531,8 @@ private static function getConditionalSanitizedTypeTokens( } /** - * @param array> $class_template_types - * @param array> $function_template_types + * @param array> $class_template_types + * @param array> $function_template_types * @param array $type_aliases * @return non-empty-list|null */ @@ -599,14 +610,14 @@ private static function getAssertionParts( $assertion_type_parts = []; foreach ($namespaced_type->getAtomicTypes() as $namespaced_type_part) { - if ($namespaced_type_part instanceof Type\Atomic\TAssertionFalsy - || $namespaced_type_part instanceof Type\Atomic\TClassConstant - || ($namespaced_type_part instanceof Type\Atomic\TList + if ($namespaced_type_part instanceof TAssertionFalsy + || $namespaced_type_part instanceof TClassConstant + || ($namespaced_type_part instanceof TList && $namespaced_type_part->type_param->isMixed()) - || ($namespaced_type_part instanceof Type\Atomic\TArray + || ($namespaced_type_part instanceof TArray && $namespaced_type_part->type_params[0]->isArrayKey() && $namespaced_type_part->type_params[1]->isMixed()) - || ($namespaced_type_part instanceof Type\Atomic\TIterable + || ($namespaced_type_part instanceof TIterable && $namespaced_type_part->type_params[0]->isMixed() && $namespaced_type_part->type_params[1]->isMixed()) ) { @@ -620,8 +631,8 @@ private static function getAssertionParts( } /** - * @param array> $class_template_types - * @param array> $function_template_types + * @param array> $class_template_types + * @param array> $function_template_types * @param array $type_aliases * @param array< * int, @@ -681,7 +692,7 @@ private static function improveParamsFromDocblock( } if (!$fake_method) { - $docblock_type_location = new CodeLocation\DocblockTypeLocation( + $docblock_type_location = new DocblockTypeLocation( $file_scanner, $docblock_param['start'], $docblock_param['end'], @@ -777,13 +788,13 @@ private static function improveParamsFromDocblock( if (!$docblock_param_variadic && $storage_param->is_variadic && $new_param_type->hasArray()) { /** * @psalm-suppress PossiblyUndefinedStringArrayOffset - * @var Type\Atomic\TArray|Type\Atomic\TKeyedArray|Type\Atomic\TList + * @var TArray|TKeyedArray|TList */ $array_type = $new_param_type->getAtomicTypes()['array']; - if ($array_type instanceof Type\Atomic\TKeyedArray) { + if ($array_type instanceof TKeyedArray) { $new_param_type = $array_type->getGenericValueType(); - } elseif ($array_type instanceof Type\Atomic\TList) { + } elseif ($array_type instanceof TList) { $new_param_type = $array_type->type_param; } else { $new_param_type = $array_type->type_params[1]; @@ -801,13 +812,13 @@ private static function improveParamsFromDocblock( && !$new_param_type->isNullable() && !$new_param_type->hasTemplate() ) { - $new_param_type->addType(new Type\Atomic\TNull()); + $new_param_type->addType(new TNull()); } $config = Config::getInstance(); if ($config->add_param_default_to_docblock_type - && $storage_param->default_type instanceof Type\Union + && $storage_param->default_type instanceof Union && !$storage_param->default_type->hasMixed() && (!$storage_param->type || !$storage_param->type->hasMixed()) ) { @@ -827,8 +838,8 @@ private static function improveParamsFromDocblock( if (isset($storage_param_atomic_types[$key])) { $type->from_docblock = false; - if ($storage_param_atomic_types[$key] instanceof Type\Atomic\TArray - && $type instanceof Type\Atomic\TArray + if ($storage_param_atomic_types[$key] instanceof TArray + && $type instanceof TArray && $type->type_params[0]->hasArrayKey() ) { $type->type_params[0]->from_docblock = false; @@ -843,7 +854,7 @@ private static function improveParamsFromDocblock( } if ($existing_param_type_nullable && !$new_param_type->isNullable()) { - $new_param_type->addType(new Type\Atomic\TNull()); + $new_param_type->addType(new TNull()); } $storage_param->type = $new_param_type; @@ -864,8 +875,8 @@ function (FunctionLikeParameter $p): bool { /** * @param array $type_aliases - * @param array> $function_template_types - * @param array> $class_template_types + * @param array> $function_template_types + * @param array> $class_template_types */ private static function handleReturn( Codebase $codebase, @@ -888,7 +899,7 @@ private static function handleReturn( && $docblock_info->return_type_start && $docblock_info->return_type_end ) { - $storage->return_type_location = new CodeLocation\DocblockTypeLocation( + $storage->return_type_location = new DocblockTypeLocation( $file_scanner, $docblock_info->return_type_start, $docblock_info->return_type_end, @@ -967,7 +978,7 @@ private static function handleReturn( $storage->signature_return_type ) ) { - $storage->return_type->addType(new Type\Atomic\TNull()); + $storage->return_type->addType(new TNull()); } } } @@ -1083,8 +1094,8 @@ private static function handleTaintFlow( /** * @param array $type_aliases - * @param array> $function_template_types - * @param array> $class_template_types + * @param array> $function_template_types + * @param array> $class_template_types */ private static function handleRemovedTaint( Codebase $codebase, @@ -1123,7 +1134,7 @@ private static function handleRemovedTaint( $removed_taint_single = $removed_taint->getSingleAtomic(); - if (!$removed_taint_single instanceof Type\Atomic\TConditional) { + if (!$removed_taint_single instanceof TConditional) { throw new TypeParseTreeException('Escaped taint must be a conditional'); } @@ -1138,8 +1149,8 @@ private static function handleRemovedTaint( /** * @param array $type_aliases - * @param array> $function_template_types - * @param array> $class_template_types + * @param array> $function_template_types + * @param array> $class_template_types */ private static function handleAssertions( FunctionDocblockComment $docblock_info, @@ -1298,8 +1309,8 @@ private static function handleAssertions( /** * @param array $type_aliases - * @param array> $function_template_types - * @param array> $class_template_types + * @param array> $function_template_types + * @param array> $class_template_types * @param array{name:string, type:string, line_number: int} $docblock_param_out */ private static function handleParamOut( @@ -1352,9 +1363,9 @@ private static function handleParamOut( } /** - * @param ?array> $template_types + * @param ?array> $template_types * @param array $type_aliases - * @return array> + * @return array> */ private static function handleTemplates( FunctionLikeStorage $storage, diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index a60d37eeb52..36b9c545187 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -1,4 +1,5 @@ > + * @var array> */ private $existing_function_template_types; @@ -112,7 +117,7 @@ class FunctionLikeNodeScanner public $storage; /** - * @param array> $existing_function_template_types + * @param array> $existing_function_template_types * @param array $type_aliases */ public function __construct( @@ -723,7 +728,7 @@ public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = fal } if ($attribute->fq_class_name === 'JetBrains\\PhpStorm\\NoReturn') { - $storage->return_type = new Type\Union([new Type\Atomic\TNever()]); + $storage->return_type = new Union([new TNever()]); } $storage->attributes[] = $attribute; @@ -774,8 +779,8 @@ private function inferPropertyTypeFromConstructor( $assigned_properties[$property_name] = $storage->params[$param_index]->is_variadic - ? new Type\Union([ - new Type\Atomic\TArray([ + ? new Union([ + new TArray([ Type::getInt(), $param_type, ]), @@ -829,7 +834,7 @@ private function getTranslatedFunctionParam( ); if ($is_nullable) { - $param_type->addType(new Type\Atomic\TNull); + $param_type->addType(new TNull); } else { $is_nullable = $param_type->isNullable(); } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php b/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php index dca3e518c3f..3dfb0859075 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php @@ -1,4 +1,5 @@ addType(new Type\Atomic\TNull); + $type->addType(new TNull); } return $type; diff --git a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php index 0a17be159ed..e99259cfca0 100644 --- a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php @@ -1,4 +1,5 @@ + * @var array */ private $functionlike_node_scanners = []; /** - * @var array + * @var array */ private $classlike_node_scanners = []; @@ -134,7 +139,7 @@ public function enterNode(PhpParser\Node $node): ?int foreach ($node->getComments() as $comment) { if ($comment instanceof PhpParser\Comment\Doc && !$node instanceof PhpParser\Node\Stmt\ClassLike) { try { - $type_aliases = Reflector\ClassLikeNodeScanner::getTypeAliasesFromComment( + $type_aliases = ClassLikeNodeScanner::getTypeAliasesFromComment( $comment, $this->aliases, $this->type_aliases, @@ -167,7 +172,7 @@ public function enterNode(PhpParser\Node $node): ?int return null; } - $classlike_node_scanner = new Reflector\ClassLikeNodeScanner( + $classlike_node_scanner = new ClassLikeNodeScanner( $this->codebase, $this->file_storage, $this->file_scanner, @@ -217,7 +222,7 @@ public function enterNode(PhpParser\Node $node): ?int $functionlike_types += $functionlike_storage->template_types ?? []; } - $functionlike_node_scanner = new Reflector\FunctionLikeNodeScanner( + $functionlike_node_scanner = new FunctionLikeNodeScanner( $this->codebase, $this->file_scanner, $this->file_storage, @@ -298,7 +303,7 @@ public function enterNode(PhpParser\Node $node): ?int if (!$this->functionlike_node_scanners) { $this->exists_cond_expr = $node->cond; - if (Reflector\ExpressionResolver::enterConditional( + if (ExpressionResolver::enterConditional( $this->codebase, $this->file_path, $this->exists_cond_expr @@ -314,7 +319,7 @@ public function enterNode(PhpParser\Node $node): ?int $this->exists_cond_expr = null; } elseif (!$this->skip_if_descendants) { if ($this->exists_cond_expr - && Reflector\ExpressionResolver::enterConditional( + && ExpressionResolver::enterConditional( $this->codebase, $this->file_path, $this->exists_cond_expr @@ -331,7 +336,7 @@ public function enterNode(PhpParser\Node $node): ?int $functionlike_storage = $functionlike_node_scanner->storage; } - Reflector\ExpressionScanner::scan( + ExpressionScanner::scan( $this->codebase, $this->file_scanner, $this->file_storage, diff --git a/src/Psalm/Internal/PhpVisitor/ShortClosureVisitor.php b/src/Psalm/Internal/PhpVisitor/ShortClosureVisitor.php index f497a305fc8..e0ed99dbbd2 100644 --- a/src/Psalm/Internal/PhpVisitor/ShortClosureVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/ShortClosureVisitor.php @@ -1,4 +1,5 @@ $file_extensions + * @param null|callable(string):bool $directory_filter * * @return list */ - public function getFilesInDir(string $dir_path, array $file_extensions): array + public function getFilesInDir(string $dir_path, array $file_extensions, callable $directory_filter = null): array { - $file_paths = parent::getFilesInDir($dir_path, $file_extensions); + $file_paths = parent::getFilesInDir($dir_path, $file_extensions, $directory_filter); foreach ($this->fake_files as $file_path => $_) { if (strpos(strtolower($file_path), strtolower($dir_path)) === 0) { diff --git a/src/Psalm/Internal/Provider/FileProvider.php b/src/Psalm/Internal/Provider/FileProvider.php index 5ca384b7fb3..161b9dc91f4 100644 --- a/src/Psalm/Internal/Provider/FileProvider.php +++ b/src/Psalm/Internal/Provider/FileProvider.php @@ -1,7 +1,11 @@ $file_extensions + * @param null|callable(string):bool $directory_filter * * @return list */ - public function getFilesInDir(string $dir_path, array $file_extensions): array + public function getFilesInDir(string $dir_path, array $file_extensions, callable $directory_filter = null): array { $file_paths = []; + $iterator = new RecursiveDirectoryIterator( + $dir_path, + FilesystemIterator::CURRENT_AS_PATHNAME | FilesystemIterator::SKIP_DOTS + ); + + if ($directory_filter !== null) { + $iterator = new RecursiveCallbackFilterIterator( + $iterator, + /** @param mixed $_ */ + function (string $current, $_, RecursiveIterator $iterator) use ($directory_filter): bool { + return !$iterator->hasChildren() || $directory_filter($current . DIRECTORY_SEPARATOR); + } + ); + } + /** @var RecursiveDirectoryIterator */ - $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir_path)); + $iterator = new RecursiveIteratorIterator($iterator); $iterator->rewind(); while ($iterator->valid()) { - if (!$iterator->isDot()) { - $extension = $iterator->getExtension(); - if (in_array($extension, $file_extensions, true)) { - $file_paths[] = (string)$iterator->getRealPath(); - } + $extension = $iterator->getExtension(); + if (in_array($extension, $file_extensions, true)) { + $file_paths[] = (string)$iterator->getRealPath(); } $iterator->next(); diff --git a/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php b/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php index af84e9b8d64..1d80b56f78e 100644 --- a/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php +++ b/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php @@ -1,4 +1,5 @@ + * array * > */ private static $handlers = []; @@ -34,7 +67,7 @@ class FunctionReturnTypeProvider * list, * Context, * CodeLocation - * ): ?Type\Union> + * ): ?Union> * > */ private static $legacy_handlers = []; @@ -44,38 +77,38 @@ public function __construct() self::$handlers = []; self::$legacy_handlers = []; - $this->registerClass(ReturnTypeProvider\ArrayChunkReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayColumnReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayFilterReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayMapReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayMergeReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayPadReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayPointerAdjustmentReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayPopReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayRandReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayReduceReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArraySliceReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArraySpliceReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayReverseReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayUniqueReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayValuesReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ArrayFillReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\FilterVarReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\IteratorToArrayReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ParseUrlReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\StrReplaceReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\StrTrReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\VersionCompareReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\MktimeReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ExplodeReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\GetObjectVarsReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\GetClassMethodsReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\FirstArgStringReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\HexdecReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\MinMaxReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\TriggerErrorReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\RandReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\InArrayReturnTypeProvider::class); + $this->registerClass(ArrayChunkReturnTypeProvider::class); + $this->registerClass(ArrayColumnReturnTypeProvider::class); + $this->registerClass(ArrayFilterReturnTypeProvider::class); + $this->registerClass(ArrayMapReturnTypeProvider::class); + $this->registerClass(ArrayMergeReturnTypeProvider::class); + $this->registerClass(ArrayPadReturnTypeProvider::class); + $this->registerClass(ArrayPointerAdjustmentReturnTypeProvider::class); + $this->registerClass(ArrayPopReturnTypeProvider::class); + $this->registerClass(ArrayRandReturnTypeProvider::class); + $this->registerClass(ArrayReduceReturnTypeProvider::class); + $this->registerClass(ArraySliceReturnTypeProvider::class); + $this->registerClass(ArraySpliceReturnTypeProvider::class); + $this->registerClass(ArrayReverseReturnTypeProvider::class); + $this->registerClass(ArrayUniqueReturnTypeProvider::class); + $this->registerClass(ArrayValuesReturnTypeProvider::class); + $this->registerClass(ArrayFillReturnTypeProvider::class); + $this->registerClass(FilterVarReturnTypeProvider::class); + $this->registerClass(IteratorToArrayReturnTypeProvider::class); + $this->registerClass(ParseUrlReturnTypeProvider::class); + $this->registerClass(StrReplaceReturnTypeProvider::class); + $this->registerClass(StrTrReturnTypeProvider::class); + $this->registerClass(VersionCompareReturnTypeProvider::class); + $this->registerClass(MktimeReturnTypeProvider::class); + $this->registerClass(ExplodeReturnTypeProvider::class); + $this->registerClass(GetObjectVarsReturnTypeProvider::class); + $this->registerClass(GetClassMethodsReturnTypeProvider::class); + $this->registerClass(FirstArgStringReturnTypeProvider::class); + $this->registerClass(HexdecReturnTypeProvider::class); + $this->registerClass(MinMaxReturnTypeProvider::class); + $this->registerClass(TriggerErrorReturnTypeProvider::class); + $this->registerClass(RandReturnTypeProvider::class); + $this->registerClass(InArrayReturnTypeProvider::class); } /** @@ -100,7 +133,7 @@ public function registerClass(string $class): void /** * @param lowercase-string $function_id - * @param Closure(FunctionReturnTypeProviderEvent): ?Type\Union $c + * @param Closure(FunctionReturnTypeProviderEvent): ?Union $c */ public function registerClosure(string $function_id, Closure $c): void { @@ -115,7 +148,7 @@ public function registerClosure(string $function_id, Closure $c): void * list, * Context, * CodeLocation - * ): ?Type\Union $c + * ): ?Union $c */ public function registerLegacyClosure(string $function_id, Closure $c): void { @@ -137,7 +170,7 @@ public function getReturnType( PhpParser\Node\Expr\FuncCall $stmt, Context $context, CodeLocation $code_location - ): ?Type\Union { + ): ?Union { foreach (self::$legacy_handlers[strtolower($function_id)] ?? [] as $function_handler) { $return_type = $function_handler( $statements_source, diff --git a/src/Psalm/Internal/Provider/MethodExistenceProvider.php b/src/Psalm/Internal/Provider/MethodExistenceProvider.php index 95318dba6dd..0f637728ca6 100644 --- a/src/Psalm/Internal/Provider/MethodExistenceProvider.php +++ b/src/Psalm/Internal/Provider/MethodExistenceProvider.php @@ -1,4 +1,5 @@ registerClass(ReturnTypeProvider\PdoStatementSetFetchMode::class); + $this->registerClass(PdoStatementSetFetchMode::class); } /** diff --git a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php index 8d91ffffca4..f29dc77d6cc 100644 --- a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php @@ -1,15 +1,21 @@ + * array * > */ private static $handlers = []; @@ -34,10 +40,10 @@ class MethodReturnTypeProvider * list, * Context, * CodeLocation, - * ?array=, + * ?array=, * ?string=, * ?lowercase-string= - * ): ?Type\Union> + * ): ?Union> * > */ private static $legacy_handlers = []; @@ -47,11 +53,11 @@ public function __construct() self::$handlers = []; self::$legacy_handlers = []; - $this->registerClass(ReturnTypeProvider\DomNodeAppendChild::class); - $this->registerClass(ReturnTypeProvider\ImagickPixelColorReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\SimpleXmlElementAsXml::class); - $this->registerClass(ReturnTypeProvider\PdoStatementReturnTypeProvider::class); - $this->registerClass(ReturnTypeProvider\ClosureFromCallableReturnTypeProvider::class); + $this->registerClass(DomNodeAppendChild::class); + $this->registerClass(ImagickPixelColorReturnTypeProvider::class); + $this->registerClass(SimpleXmlElementAsXml::class); + $this->registerClass(PdoStatementReturnTypeProvider::class); + $this->registerClass(ClosureFromCallableReturnTypeProvider::class); } /** @@ -75,7 +81,7 @@ public function registerClass(string $class): void } /** - * @param Closure(MethodReturnTypeProviderEvent): ?Type\Union $c + * @param Closure(MethodReturnTypeProviderEvent): ?Union $c */ public function registerClosure(string $fq_classlike_name, Closure $c): void { @@ -90,10 +96,10 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void * list, * Context, * CodeLocation, - * ?array=, + * ?array=, * ?string=, * ?lowercase-string= - * ): ?Type\Union $c + * ): ?Union $c * */ public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void @@ -109,7 +115,7 @@ public function has(string $fq_classlike_name): bool /** * @param PhpParser\Node\Expr\MethodCall|PhpParser\Node\Expr\StaticCall $stmt - * @param ?array $template_type_parameters + * @param ?array $template_type_parameters */ public function getReturnType( StatementsSource $statements_source, @@ -121,7 +127,7 @@ public function getReturnType( ?array $template_type_parameters = null, ?string $called_fq_classlike_name = null, ?string $called_method_name = null - ): ?Type\Union { + ): ?Union { foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $class_handler) { $result = $class_handler( $statements_source, diff --git a/src/Psalm/Internal/Provider/MethodVisibilityProvider.php b/src/Psalm/Internal/Provider/MethodVisibilityProvider.php index 96b5e2e8211..e9a876bc638 100644 --- a/src/Psalm/Internal/Provider/MethodVisibilityProvider.php +++ b/src/Psalm/Internal/Provider/MethodVisibilityProvider.php @@ -1,4 +1,5 @@ + * array * > */ private static $handlers = []; @@ -32,7 +33,7 @@ class PropertyTypeProvider * bool, * ?StatementsSource=, * ?Context= - * ): ?Type\Union> + * ): ?Union> * > */ private static $legacy_handlers = []; @@ -66,7 +67,7 @@ public function registerClass(string $class): void } /** - * @param Closure(PropertyTypeProviderEvent): ?Type\Union $c + * @param Closure(PropertyTypeProviderEvent): ?Union $c */ public function registerClosure(string $fq_classlike_name, Closure $c): void { @@ -80,7 +81,7 @@ public function registerClosure(string $fq_classlike_name, Closure $c): void * bool, * ?StatementsSource=, * ?Context= - * ): ?Type\Union $c + * ): ?Union $c */ public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void { @@ -99,7 +100,7 @@ public function getPropertyType( bool $read_mode, ?StatementsSource $source = null, ?Context $context = null - ): ?Type\Union { + ): ?Union { if ($source) { $source->addSuppressedIssues(['NonInvariantDocblockPropertyType']); diff --git a/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php b/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php index f49d952dc0f..2b127672ac0 100644 --- a/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php +++ b/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php @@ -1,4 +1,6 @@ -getCallArgs(); $statements_source = $event->getStatementsSource(); @@ -33,17 +39,17 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && ($preserve_keys_arg_type = $statements_source->getNodeTypeProvider()->getType($call_args[2]->value)) && (string) $preserve_keys_arg_type !== 'false'; - return new Type\Union([ - new Type\Atomic\TList( - new Type\Union([ + return new Union([ + new TList( + new Union([ $preserve_keys - ? new Type\Atomic\TNonEmptyArray([$array_type->key, $array_type->value]) - : new Type\Atomic\TNonEmptyList($array_type->value) + ? new TNonEmptyArray([$array_type->key, $array_type->value]) + : new TNonEmptyList($array_type->value) ]) ) ]); } - return new Type\Union([new Type\Atomic\TList(Type::getArray())]); + return new Union([new TList(Type::getArray())]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php index 9db375bb311..36e15cb9bd8 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php @@ -1,10 +1,17 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -39,11 +46,11 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev ) { $input_array = $first_arg_type->getAtomicTypes()['array']; $row_type = null; - if ($input_array instanceof Type\Atomic\TKeyedArray) { + if ($input_array instanceof TKeyedArray) { $row_type = $input_array->getGenericArrayType()->type_params[1]; - } elseif ($input_array instanceof Type\Atomic\TArray) { + } elseif ($input_array instanceof TArray) { $row_type = $input_array->type_params[1]; - } elseif ($input_array instanceof Type\Atomic\TList) { + } elseif ($input_array instanceof TList) { $row_type = $input_array->type_param; } @@ -64,9 +71,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } } - $input_array_not_empty = $input_array instanceof Type\Atomic\TNonEmptyList || - $input_array instanceof Type\Atomic\TNonEmptyArray || - $input_array instanceof Type\Atomic\TKeyedArray; + $input_array_not_empty = $input_array instanceof TNonEmptyList || + $input_array instanceof TNonEmptyArray || + $input_array instanceof TKeyedArray; } $value_column_name = null; @@ -98,7 +105,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $result_element_type = null; $have_at_least_one_res = false; // calculate results - if ($row_shape instanceof Type\Atomic\TKeyedArray) { + if ($row_shape instanceof TKeyedArray) { if ((null !== $value_column_name) && isset($row_shape->properties[$value_column_name])) { $result_element_type = $row_shape->properties[$value_column_name]; // When the selected key is possibly_undefined, the resulting array can be empty @@ -118,14 +125,14 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if (isset($call_args[2]) && (string)$third_arg_type !== 'null') { $type = $have_at_least_one_res ? - new Type\Atomic\TNonEmptyArray([$result_key_type, $result_element_type ?? Type::getMixed()]) - : new Type\Atomic\TArray([$result_key_type, $result_element_type ?? Type::getMixed()]); + new TNonEmptyArray([$result_key_type, $result_element_type ?? Type::getMixed()]) + : new TArray([$result_key_type, $result_element_type ?? Type::getMixed()]); } else { $type = $have_at_least_one_res ? - new Type\Atomic\TNonEmptyList($result_element_type ?? Type::getMixed()) - : new Type\Atomic\TList($result_element_type ?? Type::getMixed()); + new TNonEmptyList($result_element_type ?? Type::getMixed()) + : new TList($result_element_type ?? Type::getMixed()); } - return new Type\Union([$type]); + return new Union([$type]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php index 1ade25103b0..9a16f980eee 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php @@ -1,10 +1,17 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -37,15 +44,15 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($second_arg_type && self::isPositiveNumericType($second_arg_type) ) { - return new Type\Union([ - new Type\Atomic\TNonEmptyList( + return new Union([ + new TNonEmptyList( $value_type_from_third_arg ) ]); } - return new Type\Union([ - new Type\Atomic\TList( + return new Union([ + new TList( $value_type_from_third_arg ) ]); @@ -58,9 +65,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && $first_arg_type->isSingleIntLiteral() && $second_arg_type->isSingleIntLiteral() ) { - return new Type\Union([ - new Type\Atomic\TNonEmptyArray([ - new Type\Union([new Type\Atomic\TIntRange( + return new Union([ + new TNonEmptyArray([ + new Union([new TIntRange( $first_arg_type->getSingleIntLiteral()->value, $second_arg_type->getSingleIntLiteral()->value )]), @@ -69,23 +76,23 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev ]); } - return new Type\Union([ - new Type\Atomic\TNonEmptyArray([ + return new Union([ + new TNonEmptyArray([ Type::getInt(), $value_type_from_third_arg, ]) ]); } - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ Type::getInt(), $value_type_from_third_arg, ]) ]); } - private static function isPositiveNumericType(Type\Union $arg): bool + private static function isPositiveNumericType(Union $arg): bool { if ($arg->isSingle() && $arg->hasPositiveInt()) { return true; diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php index bea34669a54..6cc324213dd 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -53,9 +61,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && ($first_arg_type = $statements_source->node_data->getType($array_arg)) && $first_arg_type->hasType('array') && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array']) - && ($array_atomic_type instanceof Type\Atomic\TArray - || $array_atomic_type instanceof Type\Atomic\TKeyedArray - || $array_atomic_type instanceof Type\Atomic\TList) + && ($array_atomic_type instanceof TArray + || $array_atomic_type instanceof TKeyedArray + || $array_atomic_type instanceof TList) ? $array_atomic_type : null; @@ -63,10 +71,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return Type::getArray(); } - if ($first_arg_array instanceof Type\Atomic\TArray) { + if ($first_arg_array instanceof TArray) { $inner_type = $first_arg_array->type_params[1]; $key_type = clone $first_arg_array->type_params[0]; - } elseif ($first_arg_array instanceof Type\Atomic\TList) { + } elseif ($first_arg_array instanceof TList) { $inner_type = $first_arg_array->type_param; $key_type = Type::getInt(); } else { @@ -114,7 +122,7 @@ static function ($keyed_type) { $first_arg_array->is_list = $first_arg_array->is_list && $had_one; $first_arg_array->sealed = false; - return new Type\Union([$first_arg_array]); + return new Union([$first_arg_array]); } } @@ -130,24 +138,24 @@ static function ($keyed_type) { $statements_source->getSuppressedIssues() ); - if ($first_arg_array instanceof Type\Atomic\TKeyedArray + if ($first_arg_array instanceof TKeyedArray && $first_arg_array->is_list && $key_type->isSingleIntLiteral() && $key_type->getSingleIntLiteral()->value === 0 ) { - return new Type\Union([ - new Type\Atomic\TList( + return new Union([ + new TList( $inner_type ), ]); } if ($key_type->getLiteralStrings()) { - $key_type->addType(new Type\Atomic\TString); + $key_type->addType(new TString); } if ($key_type->getLiteralInts()) { - $key_type->addType(new Type\Atomic\TInt); + $key_type->addType(new TInt); } /** @psalm-suppress TypeDoesNotContainType can be empty after removing above */ @@ -155,8 +163,8 @@ static function ($keyed_type) { return Type::getEmptyArray(); } - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ $key_type, $inner_type, ]), @@ -178,13 +186,15 @@ static function ($keyed_type) { if ($array_arg && $mapping_function_ids) { $assertions = []; + $fake_var_discriminator = mt_rand(); ArrayMapReturnTypeProvider::getReturnTypeFromMappingIds( $statements_source, $mapping_function_ids, $context, $function_call_arg, array_slice($call_args, 0, 1), - $assertions + $assertions, + $fake_var_discriminator ); $array_var_id = ExpressionIdentifier::getArrayVarId( @@ -193,10 +203,13 @@ static function ($keyed_type) { $statements_source ); - if (isset($assertions[$array_var_id . '[$__fake_offset_var__]'])) { + if (isset($assertions[$array_var_id . "[\$__fake_{$fake_var_discriminator}_offset_var__]"])) { $changed_var_ids = []; - $assertions = ['$inner_type' => $assertions[$array_var_id . '[$__fake_offset_var__]']]; + $assertions = [ + '$inner_type' => + $assertions["{$array_var_id}[\$__fake_{$fake_var_discriminator}_offset_var__]"], + ]; $reconciled_types = Reconciler::reconcileKeyedTypes( $assertions, @@ -214,6 +227,8 @@ static function ($keyed_type) { $inner_type = $reconciled_types['$inner_type']; } } + + ArrayMapReturnTypeProvider::cleanContext($context, $fake_var_discriminator); } } elseif (($function_call_arg->value instanceof PhpParser\Node\Expr\Closure || $function_call_arg->value instanceof PhpParser\Node\Expr\ArrowFunction) @@ -295,8 +310,8 @@ static function ($keyed_type) { } } - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ $key_type, $inner_type, ]), @@ -308,8 +323,8 @@ static function ($keyed_type) { return Type::getEmptyArray(); } - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ $key_type, $inner_type, ]), diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php index 57b8d3cb6c2..4437ff4627f 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -75,7 +87,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } if ($array_arg_types) { - return new Type\Union([new Type\Atomic\TKeyedArray($array_arg_types)]); + return new Union([new TKeyedArray($array_arg_types)]); } return Type::getArray(); @@ -150,15 +162,15 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $fake_method_call = null; foreach ($variable_type->getAtomicTypes() as $variable_atomic_type) { - if ($variable_atomic_type instanceof Type\Atomic\TTemplateParam - || $variable_atomic_type instanceof Type\Atomic\TTemplateParamClass + if ($variable_atomic_type instanceof TTemplateParam + || $variable_atomic_type instanceof TTemplateParamClass ) { $fake_method_call = new VirtualStaticCall( $function_call_arg->value->items[0]->value, $function_call_arg->value->items[1]->value->value, [] ); - } elseif ($variable_atomic_type instanceof Type\Atomic\TTemplateParamClass) { + } elseif ($variable_atomic_type instanceof TTemplateParamClass) { $fake_method_call = new VirtualStaticCall( $function_call_arg->value->items[0]->value, $function_call_arg->value->items[1]->value->value, @@ -183,13 +195,13 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } if ($mapping_return_type && $generic_key_type) { - if ($array_arg_atomic_type instanceof Type\Atomic\TKeyedArray && count($call_args) === 2) { - $atomic_type = new Type\Atomic\TKeyedArray( + if ($array_arg_atomic_type instanceof TKeyedArray && count($call_args) === 2) { + $atomic_type = new TKeyedArray( array_map( /** - * @return Type\Union + * @return Union */ - function (Type\Union $_) use ($mapping_return_type): Type\Union { + function (Union $_) use ($mapping_return_type): Union { return clone $mapping_return_type; }, $array_arg_atomic_type->properties @@ -200,38 +212,38 @@ function (Type\Union $_) use ($mapping_return_type): Type\Union { $atomic_type->previous_key_type = $array_arg_atomic_type->previous_key_type; $atomic_type->previous_value_type = $mapping_return_type; - return new Type\Union([$atomic_type]); + return new Union([$atomic_type]); } - if ($array_arg_atomic_type instanceof Type\Atomic\TList + if ($array_arg_atomic_type instanceof TList || count($call_args) !== 2 ) { - if ($array_arg_atomic_type instanceof Type\Atomic\TNonEmptyList) { - return new Type\Union([ - new Type\Atomic\TNonEmptyList( + if ($array_arg_atomic_type instanceof TNonEmptyList) { + return new Union([ + new TNonEmptyList( $mapping_return_type ), ]); } - return new Type\Union([ - new Type\Atomic\TList( + return new Union([ + new TList( $mapping_return_type ), ]); } - if ($array_arg_atomic_type instanceof Type\Atomic\TNonEmptyArray) { - return new Type\Union([ - new Type\Atomic\TNonEmptyArray([ + if ($array_arg_atomic_type instanceof TNonEmptyArray) { + return new Union([ + new TNonEmptyArray([ $generic_key_type, $mapping_return_type, ]), ]); } - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ $generic_key_type, $mapping_return_type, ]) @@ -239,8 +251,8 @@ function (Type\Union $_) use ($mapping_return_type): Type\Union { } return count($call_args) === 2 && !($array_arg_type->is_list ?? false) - ? new Type\Union([ - new Type\Atomic\TArray([ + ? new Union([ + new TArray([ $array_arg_type->key ?? Type::getArrayKey(), Type::getMixed(), ]) @@ -256,7 +268,7 @@ private static function executeFakeCall( PhpParser\Node\Expr $fake_call, Context $context, ?array &$assertions = null - ): ?Type\Union { + ): ?Union { $old_data_provider = $statements_analyzer->node_data; $statements_analyzer->node_data = clone $statements_analyzer->node_data; @@ -330,6 +342,8 @@ private static function executeFakeCall( /** * @param non-empty-array $mapping_function_ids * @param list $array_args + * @param int|null $fake_var_discriminator Set the fake variable id to a known value with the discriminator + * as a substring, and don't clear it from the context. * @param-out array>>|null $assertions */ public static function getReturnTypeFromMappingIds( @@ -338,15 +352,23 @@ public static function getReturnTypeFromMappingIds( Context $context, PhpParser\Node\Arg $function_call_arg, array $array_args, - ?array &$assertions = null - ): Type\Union { + ?array &$assertions = null, + ?int $fake_var_discriminator = null + ): Union { $mapping_return_type = null; $codebase = $statements_source->getCodebase(); + $clean_context = false; + foreach ($mapping_function_ids as $mapping_function_id) { $mapping_function_id_parts = explode('&', $mapping_function_id); + if ($fake_var_discriminator === null) { + $fake_var_discriminator = mt_rand(); + $clean_context = true; + } + foreach ($mapping_function_id_parts as $mapping_function_id_part) { $fake_args = []; @@ -355,7 +377,7 @@ public static function getReturnTypeFromMappingIds( new VirtualArrayDimFetch( $array_arg->value, new VirtualVariable( - '__fake_offset_var__', + "__fake_{$fake_var_discriminator}_offset_var__", $array_arg->value->getAttributes() ), $array_arg->value->getAttributes() @@ -380,7 +402,7 @@ public static function getReturnTypeFromMappingIds( if ($is_instance) { $fake_method_call = new VirtualMethodCall( new VirtualVariable( - '__fake_method_call_var__', + "__fake_{$fake_var_discriminator}_method_call_var__", $function_call_arg->getAttributes() ), new VirtualIdentifier( @@ -397,7 +419,7 @@ public static function getReturnTypeFromMappingIds( if ($callable_type) { foreach ($callable_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TKeyedArray + if ($atomic_type instanceof TKeyedArray && count($atomic_type->properties) === 2 && isset($atomic_type->properties[0]) ) { @@ -406,11 +428,9 @@ public static function getReturnTypeFromMappingIds( } } - $context->vars_in_scope['$__fake_offset_var__'] = Type::getMixed(); - $context->vars_in_scope['$__fake_method_call_var__'] = $lhs_instance_type - ?: new Type\Union([ - new Type\Atomic\TNamedObject($callable_fq_class_name) - ]); + $context->vars_in_scope["\$__fake_{$fake_var_discriminator}_offset_var__"] = Type::getMixed(); + $context->vars_in_scope["\$__fake_{$fake_var_discriminator}_method_call_var__"] = + $lhs_instance_type ?: new Union([new TNamedObject($callable_fq_class_name)]); $fake_method_return_type = self::executeFakeCall( $statements_source, @@ -418,9 +438,6 @@ public static function getReturnTypeFromMappingIds( $context, $assertions ); - - unset($context->vars_in_scope['$__fake_offset_var__']); - unset($context->vars_in_scope['$__method_call_var__']); } else { $fake_method_call = new VirtualStaticCall( new VirtualFullyQualified( @@ -435,7 +452,7 @@ public static function getReturnTypeFromMappingIds( $function_call_arg->getAttributes() ); - $context->vars_in_scope['$__fake_offset_var__'] = Type::getMixed(); + $context->vars_in_scope["\$__fake_{$fake_var_discriminator}_offset_var__"] = Type::getMixed(); $fake_method_return_type = self::executeFakeCall( $statements_source, @@ -443,8 +460,6 @@ public static function getReturnTypeFromMappingIds( $context, $assertions ); - - unset($context->vars_in_scope['$__fake_offset_var__']); } $function_id_return_type = $fake_method_return_type ?? Type::getMixed(); @@ -458,7 +473,7 @@ public static function getReturnTypeFromMappingIds( $function_call_arg->getAttributes() ); - $context->vars_in_scope['$__fake_offset_var__'] = Type::getMixed(); + $context->vars_in_scope["\$__fake_{$fake_var_discriminator}_offset_var__"] = Type::getMixed(); $fake_function_return_type = self::executeFakeCall( $statements_source, @@ -467,12 +482,16 @@ public static function getReturnTypeFromMappingIds( $assertions ); - unset($context->vars_in_scope['$__fake_offset_var__']); - $function_id_return_type = $fake_function_return_type ?? Type::getMixed(); } } + if ($clean_context) { + self::cleanContext($context, $fake_var_discriminator); + } + + $fake_var_discriminator = null; + $mapping_return_type = Type::combineUnionTypes( $function_id_return_type, $mapping_return_type, @@ -482,4 +501,13 @@ public static function getReturnTypeFromMappingIds( return $mapping_return_type; } + + public static function cleanContext(Context $context, int $fake_var_discriminator): void + { + foreach ($context->vars_in_scope as $var_in_scope => $_) { + if (str_contains($var_in_scope, "__fake_{$fake_var_discriminator}_")) { + unset($context->vars_in_scope[$var_in_scope]); + } + } + } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index 12674e9d921..578560b635c 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -53,10 +64,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev foreach ($call_arg_type->getAtomicTypes() as $type_part) { if ($call_arg->unpack) { - if (!$type_part instanceof Type\Atomic\TArray) { - if ($type_part instanceof Type\Atomic\TKeyedArray) { + if (!$type_part instanceof TArray) { + if ($type_part instanceof TKeyedArray) { $type_part_value_type = $type_part->getGenericValueType(); - } elseif ($type_part instanceof Type\Atomic\TList) { + } elseif ($type_part instanceof TList) { $type_part_value_type = $type_part->type_param; } else { return Type::getArray(); @@ -75,16 +86,16 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } foreach ($unpacked_type_parts as $unpacked_type_part) { - if (!$unpacked_type_part instanceof Type\Atomic\TArray) { - if (($unpacked_type_part instanceof Type\Atomic\TFalse + if (!$unpacked_type_part instanceof TArray) { + if (($unpacked_type_part instanceof TFalse && $call_arg_type->ignore_falsable_issues) - || ($unpacked_type_part instanceof Type\Atomic\TNull + || ($unpacked_type_part instanceof TNull && $call_arg_type->ignore_nullable_issues) ) { continue; } - if ($unpacked_type_part instanceof Type\Atomic\TKeyedArray) { + if ($unpacked_type_part instanceof TKeyedArray) { $max_keyed_array_size = max( $max_keyed_array_size, count($unpacked_type_part->properties) @@ -122,19 +133,19 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev continue; } - if ($unpacked_type_part instanceof Type\Atomic\TList) { + if ($unpacked_type_part instanceof TList) { $all_keyed_arrays = false; - if (!$unpacked_type_part instanceof Type\Atomic\TNonEmptyList) { + if (!$unpacked_type_part instanceof TNonEmptyList) { $all_nonempty_lists = false; } else { $any_nonempty = true; } } else { - if ($unpacked_type_part instanceof Type\Atomic\TMixed + if ($unpacked_type_part instanceof TMixed && $unpacked_type_part->from_loop_isset ) { - $unpacked_type_part = new Type\Atomic\TArray([ + $unpacked_type_part = new TArray([ Type::getArrayKey(), Type::getMixed(true), ]); @@ -157,7 +168,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } } - if ($unpacked_type_part instanceof Type\Atomic\TArray) { + if ($unpacked_type_part instanceof TArray) { if ($unpacked_type_part->type_params[1]->isEmpty()) { continue; } @@ -166,20 +177,20 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $all_int_offsets = false; } - if ($unpacked_type_part instanceof Type\Atomic\TNonEmptyArray) { + if ($unpacked_type_part instanceof TNonEmptyArray) { $any_nonempty = true; } } $inner_key_types = array_merge( $inner_key_types, - $unpacked_type_part instanceof Type\Atomic\TList - ? [new Type\Atomic\TInt()] + $unpacked_type_part instanceof TList + ? [new TInt()] : array_values($unpacked_type_part->type_params[0]->getAtomicTypes()) ); $inner_value_types = array_merge( $inner_value_types, - $unpacked_type_part instanceof Type\Atomic\TList + $unpacked_type_part instanceof TList ? array_values($unpacked_type_part->type_param->getAtomicTypes()) : array_values($unpacked_type_part->type_params[1]->getAtomicTypes()) ); @@ -205,7 +216,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && ($generic_property_count < $max_keyed_array_size * 2 || $generic_property_count < 16) ) { - $objectlike = new Type\Atomic\TKeyedArray($generic_properties); + $objectlike = new TKeyedArray($generic_properties); if ($all_nonempty_lists || $all_int_offsets) { $objectlike->is_list = true; @@ -216,35 +227,35 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $objectlike->previous_value_type = $inner_value_type; } - return new Type\Union([$objectlike]); + return new Union([$objectlike]); } if ($inner_value_type) { if ($all_int_offsets) { if ($any_nonempty) { - return new Type\Union([ - new Type\Atomic\TNonEmptyList($inner_value_type), + return new Union([ + new TNonEmptyList($inner_value_type), ]); } - return new Type\Union([ - new Type\Atomic\TList($inner_value_type), + return new Union([ + new TList($inner_value_type), ]); } $inner_key_type = $inner_key_type ?? Type::getArrayKey(); if ($any_nonempty) { - return new Type\Union([ - new Type\Atomic\TNonEmptyArray([ + return new Union([ + new TNonEmptyArray([ $inner_key_type, $inner_value_type, ]), ]); } - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ $inner_key_type, $inner_value_type, ]), diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php index 710936eb86b..f5859c174c5 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php @@ -1,4 +1,6 @@ -getStatementsSource(); $call_args = $event->getCallArgs(); @@ -41,17 +48,17 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev || $size_arg_type->getSingleIntLiteral()->value === 0 ); - return new Type\Union([ + return new Union([ $array_type->is_list ? ( $can_return_empty - ? new Type\Atomic\TList($value_type) - : new Type\Atomic\TNonEmptyList($value_type) + ? new TList($value_type) + : new TNonEmptyList($value_type) ) : ( $can_return_empty - ? new Type\Atomic\TArray([$key_type, $value_type]) - : new Type\Atomic\TNonEmptyArray([$key_type, $value_type]) + ? new TArray([$key_type, $value_type]) + : new TNonEmptyArray([$key_type, $value_type]) ) ]); } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php index a1d0da9e6ca..66d39525603 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -48,20 +57,20 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $definitely_has_items = false; while ($atomic_type = array_shift($atomic_types)) { - if ($atomic_type instanceof Type\Atomic\TTemplateParam) { + if ($atomic_type instanceof TTemplateParam) { $atomic_types = array_merge($atomic_types, $atomic_type->as->getAtomicTypes()); continue; } - if ($atomic_type instanceof Type\Atomic\TArray) { + if ($atomic_type instanceof TArray) { $value_type = clone $atomic_type->type_params[1]; - $definitely_has_items = $atomic_type instanceof Type\Atomic\TNonEmptyArray; - } elseif ($atomic_type instanceof Type\Atomic\TList) { + $definitely_has_items = $atomic_type instanceof TNonEmptyArray; + } elseif ($atomic_type instanceof TList) { $value_type = clone $atomic_type->type_param; - $definitely_has_items = $atomic_type instanceof Type\Atomic\TNonEmptyList; - } elseif ($atomic_type instanceof Type\Atomic\TKeyedArray) { + $definitely_has_items = $atomic_type instanceof TNonEmptyList; + } elseif ($atomic_type instanceof TKeyedArray) { $value_type = $atomic_type->getGenericValueType(); - $definitely_has_items = $atomic_type->getGenericArrayType() instanceof Type\Atomic\TNonEmptyArray; + $definitely_has_items = $atomic_type->getGenericArrayType() instanceof TNonEmptyArray; } else { return Type::getMixed(); } @@ -74,7 +83,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($value_type->isEmpty()) { $value_type = Type::getFalse(); } elseif (($function_id !== 'reset' && $function_id !== 'end') || !$definitely_has_items) { - $value_type->addType(new Type\Atomic\TFalse); + $value_type->addType(new TFalse); $codebase = $statements_source->getCodebase(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php index 99c7984b96b..25dd2bd2ccc 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php @@ -1,10 +1,18 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -32,9 +40,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && $first_arg_type->hasType('array') && !$first_arg_type->hasMixed() && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array']) - && ($array_atomic_type instanceof Type\Atomic\TArray - || $array_atomic_type instanceof Type\Atomic\TKeyedArray - || $array_atomic_type instanceof Type\Atomic\TList) + && ($array_atomic_type instanceof TArray + || $array_atomic_type instanceof TKeyedArray + || $array_atomic_type instanceof TList) ? $array_atomic_type : null; @@ -44,20 +52,20 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $nullable = false; - if ($first_arg_array instanceof Type\Atomic\TArray) { + if ($first_arg_array instanceof TArray) { $value_type = clone $first_arg_array->type_params[1]; if ($value_type->isEmpty()) { return Type::getNull(); } - if (!$first_arg_array instanceof Type\Atomic\TNonEmptyArray) { + if (!$first_arg_array instanceof TNonEmptyArray) { $nullable = true; } - } elseif ($first_arg_array instanceof Type\Atomic\TList) { + } elseif ($first_arg_array instanceof TList) { $value_type = clone $first_arg_array->type_param; - if (!$first_arg_array instanceof Type\Atomic\TNonEmptyList) { + if (!$first_arg_array instanceof TNonEmptyList) { $nullable = true; } } else { @@ -74,7 +82,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } if ($nullable) { - $value_type->addType(new Type\Atomic\TNull); + $value_type->addType(new TNull); $codebase = $statements_source->getCodebase(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php index e94f7637389..8c7062b31aa 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -32,9 +37,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && ($first_arg_type = $statements_source->node_data->getType($first_arg)) && $first_arg_type->hasType('array') && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array']) - && ($array_atomic_type instanceof Type\Atomic\TArray - || $array_atomic_type instanceof Type\Atomic\TKeyedArray - || $array_atomic_type instanceof Type\Atomic\TList) + && ($array_atomic_type instanceof TArray + || $array_atomic_type instanceof TKeyedArray + || $array_atomic_type instanceof TList) ? $array_atomic_type : null; @@ -42,9 +47,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return Type::getMixed(); } - if ($first_arg_array instanceof Type\Atomic\TArray) { + if ($first_arg_array instanceof TArray) { $key_type = clone $first_arg_array->type_params[0]; - } elseif ($first_arg_array instanceof Type\Atomic\TList) { + } elseif ($first_arg_array instanceof TList) { $key_type = Type::getInt(); } else { $key_type = $first_arg_array->getGenericKeyType(); @@ -56,8 +61,8 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return $key_type; } - $arr_type = new Type\Union([ - new Type\Atomic\TList( + $arr_type = new Union([ + new TList( $key_type ), ]); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php index 3e267ee4357..a9d27ba8774 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -62,16 +67,16 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $array_arg_atomic_type = null; if (isset($array_arg_types['array']) - && ($array_arg_types['array'] instanceof Type\Atomic\TArray - || $array_arg_types['array'] instanceof Type\Atomic\TKeyedArray - || $array_arg_types['array'] instanceof Type\Atomic\TList) + && ($array_arg_types['array'] instanceof TArray + || $array_arg_types['array'] instanceof TKeyedArray + || $array_arg_types['array'] instanceof TList) ) { $array_arg_atomic_type = $array_arg_types['array']; - if ($array_arg_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($array_arg_atomic_type instanceof TKeyedArray) { $array_arg_atomic_type = $array_arg_atomic_type->getGenericArrayType(); - } elseif ($array_arg_atomic_type instanceof Type\Atomic\TList) { - $array_arg_atomic_type = new Type\Atomic\TArray([ + } elseif ($array_arg_atomic_type instanceof TList) { + $array_arg_atomic_type = new TArray([ Type::getInt(), clone $array_arg_atomic_type->type_param ]); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php index 59768aadb18..0fab0664810 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php @@ -1,10 +1,15 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -30,9 +35,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && ($first_arg_type = $statements_source->node_data->getType($first_arg)) && $first_arg_type->hasType('array') && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array']) - && ($array_atomic_type instanceof Type\Atomic\TArray - || $array_atomic_type instanceof Type\Atomic\TKeyedArray - || $array_atomic_type instanceof Type\Atomic\TList) + && ($array_atomic_type instanceof TArray + || $array_atomic_type instanceof TKeyedArray + || $array_atomic_type instanceof TList) ? $array_atomic_type : null; @@ -40,11 +45,11 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return Type::getArray(); } - if ($first_arg_array instanceof Type\Atomic\TArray) { - return new Type\Union([clone $first_arg_array]); + if ($first_arg_array instanceof TArray) { + return new Union([clone $first_arg_array]); } - if ($first_arg_array instanceof Type\Atomic\TList) { + if ($first_arg_array instanceof TList) { $second_arg = $call_args[1]->value ?? null; if (!$second_arg @@ -52,12 +57,12 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && $second_arg_type->isFalse() ) ) { - return new Type\Union([clone $first_arg_array]); + return new Union([clone $first_arg_array]); } - return new Type\Union([new Type\Atomic\TArray([Type::getInt(), clone $first_arg_array->type_param])]); + return new Union([new TArray([Type::getInt(), clone $first_arg_array->type_param])]); } - return new Type\Union([$first_arg_array->getGenericArrayType()]); + return new Union([$first_arg_array->getGenericArrayType()]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php index 35f9896fc53..399bb259319 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php @@ -1,10 +1,16 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -46,29 +52,29 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $return_atomic_type = null; while ($atomic_type = array_shift($atomic_types)) { - if ($atomic_type instanceof Type\Atomic\TTemplateParam) { + if ($atomic_type instanceof TTemplateParam) { $atomic_types = array_merge($atomic_types, $atomic_type->as->getAtomicTypes()); continue; } $already_cloned = false; - if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_type instanceof TKeyedArray) { $already_cloned = true; $atomic_type = $atomic_type->getGenericArrayType(); } - if ($atomic_type instanceof Type\Atomic\TArray) { + if ($atomic_type instanceof TArray) { if (!$already_cloned) { $atomic_type = clone $atomic_type; } - $return_atomic_type = new Type\Atomic\TArray($atomic_type->type_params); + $return_atomic_type = new TArray($atomic_type->type_params); continue; } - if ($atomic_type instanceof Type\Atomic\TList) { - $return_atomic_type = new Type\Atomic\TArray([Type::getInt(), clone $atomic_type->type_param]); + if ($atomic_type instanceof TList) { + $return_atomic_type = new TArray([Type::getInt(), clone $atomic_type->type_param]); continue; } @@ -84,9 +90,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && ((string) $third_arg_type === 'false')); if ($dont_preserve_int_keys && $return_atomic_type->type_params[0]->isInt()) { - $return_atomic_type = new Type\Atomic\TList($return_atomic_type->type_params[1]); + $return_atomic_type = new TList($return_atomic_type->type_params[1]); } - return new Type\Union([$return_atomic_type]); + return new Union([$return_atomic_type]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php index 3d6835f34a7..6f4d0625da1 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php @@ -1,10 +1,15 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -30,9 +35,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && ($first_arg_type = $statements_source->node_data->getType($first_arg)) && $first_arg_type->hasType('array') && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array']) - && ($array_atomic_type instanceof Type\Atomic\TArray - || $array_atomic_type instanceof Type\Atomic\TKeyedArray - || $array_atomic_type instanceof Type\Atomic\TList) + && ($array_atomic_type instanceof TArray + || $array_atomic_type instanceof TKeyedArray + || $array_atomic_type instanceof TList) ? $array_atomic_type : null; @@ -42,30 +47,30 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $already_cloned = false; - if ($first_arg_array instanceof Type\Atomic\TKeyedArray) { + if ($first_arg_array instanceof TKeyedArray) { $already_cloned = true; $first_arg_array = $first_arg_array->getGenericArrayType(); } - if ($first_arg_array instanceof Type\Atomic\TArray) { + if ($first_arg_array instanceof TArray) { if (!$already_cloned) { $first_arg_array = clone $first_arg_array; } - $array_type = new Type\Atomic\TArray($first_arg_array->type_params); + $array_type = new TArray($first_arg_array->type_params); } else { - $array_type = new Type\Atomic\TArray([Type::getInt(), clone $first_arg_array->type_param]); + $array_type = new TArray([Type::getInt(), clone $first_arg_array->type_param]); } if (!$array_type->type_params[0]->hasString()) { if ($array_type->type_params[1]->isString()) { - $array_type = new Type\Atomic\TList(Type::getString()); + $array_type = new TList(Type::getString()); } elseif ($array_type->type_params[1]->isInt()) { - $array_type = new Type\Atomic\TList(Type::getInt()); + $array_type = new TList(Type::getInt()); } else { - $array_type = new Type\Atomic\TList(Type::getMixed()); + $array_type = new TList(Type::getMixed()); } } - return new Type\Union([$array_type]); + return new Union([$array_type]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php index 80ca137d6cf..5499ad275d2 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php @@ -1,10 +1,17 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -30,9 +37,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && ($first_arg_type = $statements_source->node_data->getType($first_arg)) && $first_arg_type->hasType('array') && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array']) - && ($array_atomic_type instanceof Type\Atomic\TArray - || $array_atomic_type instanceof Type\Atomic\TKeyedArray - || $array_atomic_type instanceof Type\Atomic\TList) + && ($array_atomic_type instanceof TArray + || $array_atomic_type instanceof TKeyedArray + || $array_atomic_type instanceof TList) ? $array_atomic_type : null; @@ -40,34 +47,34 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return Type::getArray(); } - if ($first_arg_array instanceof Type\Atomic\TArray) { + if ($first_arg_array instanceof TArray) { $first_arg_array = clone $first_arg_array; - if ($first_arg_array instanceof Type\Atomic\TNonEmptyArray) { + if ($first_arg_array instanceof TNonEmptyArray) { $first_arg_array->count = null; } - return new Type\Union([$first_arg_array]); + return new Union([$first_arg_array]); } - if ($first_arg_array instanceof Type\Atomic\TList) { - if ($first_arg_array instanceof Type\Atomic\TNonEmptyList) { - return new Type\Union([ - new Type\Atomic\TNonEmptyArray([ + if ($first_arg_array instanceof TList) { + if ($first_arg_array instanceof TNonEmptyList) { + return new Union([ + new TNonEmptyArray([ Type::getInt(), clone $first_arg_array->type_param ]) ]); } - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ Type::getInt(), clone $first_arg_array->type_param ]) ]); } - return new Type\Union([$first_arg_array->getGenericArrayType()]); + return new Union([$first_arg_array->getGenericArrayType()]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php index 8ba6ae03b2a..0544510f8c6 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php @@ -1,10 +1,18 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -45,26 +53,26 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $return_atomic_type = null; while ($atomic_type = array_shift($atomic_types)) { - if ($atomic_type instanceof Type\Atomic\TTemplateParam) { + if ($atomic_type instanceof TTemplateParam) { $atomic_types = array_merge($atomic_types, $atomic_type->as->getAtomicTypes()); continue; } - if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_type instanceof TKeyedArray) { $atomic_type = $atomic_type->getGenericArrayType(); } - if ($atomic_type instanceof Type\Atomic\TArray) { - if ($atomic_type instanceof Type\Atomic\TNonEmptyArray) { - $return_atomic_type = new Type\Atomic\TNonEmptyList( + if ($atomic_type instanceof TArray) { + if ($atomic_type instanceof TNonEmptyArray) { + $return_atomic_type = new TNonEmptyList( clone $atomic_type->type_params[1] ); } else { - $return_atomic_type = new Type\Atomic\TList( + $return_atomic_type = new TList( clone $atomic_type->type_params[1] ); } - } elseif ($atomic_type instanceof Type\Atomic\TList) { + } elseif ($atomic_type instanceof TList) { $return_atomic_type = $atomic_type; } else { return Type::getArray(); @@ -75,6 +83,6 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev throw new UnexpectedValueException('This should never happen'); } - return new Type\Union([$return_atomic_type]); + return new Union([$return_atomic_type]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php index 32684c50dc3..a94e5817a43 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php @@ -1,4 +1,5 @@ getSource(); $method_name_lowercase = $event->getMethodNameLowercase(); @@ -43,7 +46,7 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event) ); if ($candidate_callable) { - $closure_types[] = new Type\Atomic\TClosure( + $closure_types[] = new TClosure( 'Closure', $candidate_callable->params, $candidate_callable->return_type, diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php index 7eaabcf853d..6d7723bfb0d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php @@ -1,10 +1,12 @@ getSource(); $call_args = $event->getCallArgs(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php index 271854c7b3a..81001ff95fa 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -30,10 +38,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if (count($call_args) >= 2) { $second_arg_type = $statements_source->node_data->getType($call_args[1]->value); - $inner_type = new Type\Union([ + $inner_type = new Union([ $second_arg_type && $second_arg_type->hasLowercaseString() - ? new Type\Atomic\TLowercaseString() - : new Type\Atomic\TString + ? new TLowercaseString() + : new TString ]); $can_return_empty = isset($call_args[2]) @@ -47,10 +55,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return Type::getFalse(); } - return new Type\Union([ + return new Union([ $can_return_empty - ? new Type\Atomic\TList($inner_type) - : new Type\Atomic\TNonEmptyList($inner_type) + ? new TList($inner_type) + : new TNonEmptyList($inner_type) ]); } @@ -60,28 +68,28 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($first_arg_type->isString()) { $can_be_false = false; foreach ($first_arg_type->getAtomicTypes() as $string_type) { - if (!($string_type instanceof Type\Atomic\TNonEmptyString)) { + if (!($string_type instanceof TNonEmptyString)) { $can_be_false = true; break; } } } if ($can_be_false) { - $array_type = new Type\Union([ + $array_type = new Union([ $can_return_empty - ? new Type\Atomic\TList($inner_type) - : new Type\Atomic\TNonEmptyList($inner_type), - new Type\Atomic\TFalse + ? new TList($inner_type) + : new TNonEmptyList($inner_type), + new TFalse ]); if ($statements_source->getCodebase()->config->ignore_internal_falsable_issues) { $array_type->ignore_falsable_issues = true; } } else { - $array_type = new Type\Union([ + $array_type = new Union([ $can_return_empty - ? new Type\Atomic\TList($inner_type) - : new Type\Atomic\TNonEmptyList($inner_type), + ? new TList($inner_type) + : new TNonEmptyList($inner_type), ]); } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php index 6c99c67118f..8be97ef129b 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -83,13 +89,13 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev && $filter_type ) { foreach ($third_arg_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_type instanceof TKeyedArray) { $has_object_like = true; if (isset($atomic_type->properties['options']) && $atomic_type->properties['options']->hasArray() && ($options_array = $atomic_type->properties['options']->getAtomicTypes()['array']) - && $options_array instanceof Type\Atomic\TKeyedArray + && $options_array instanceof TKeyedArray && isset($options_array->properties['default']) ) { $filter_type = Type::combineUnionTypes( @@ -97,7 +103,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $options_array->properties['default'] ); } else { - $filter_type->addType(new Type\Atomic\TFalse); + $filter_type->addType(new TFalse); } if (isset($atomic_type->properties['flags']) @@ -109,20 +115,20 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($filter_type->hasBool() && $filter_flag_type->value === FILTER_NULL_ON_FAILURE ) { - $filter_type->addType(new Type\Atomic\TNull); + $filter_type->addType(new TNull); } } - } elseif ($atomic_type instanceof Type\Atomic\TLiteralInt) { + } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value === FILTER_NULL_ON_FAILURE) { $filter_null = true; - $filter_type->addType(new Type\Atomic\TNull); + $filter_type->addType(new TNull); } } } } if (!$has_object_like && !$filter_null && $filter_type) { - $filter_type->addType(new Type\Atomic\TFalse); + $filter_type->addType(new TFalse); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php index 95e521c9980..4f99fdddb2d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php @@ -1,10 +1,13 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -36,7 +39,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return $return_type; } - $return_type->addType(new Type\Atomic\TNull); + $return_type->addType(new TNull); $return_type->ignore_nullable_issues = true; return $return_type; diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php index 9e0a729d8b7..4c7d78655a4 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php @@ -1,10 +1,12 @@ getStatementsSource(); $call_args = $event->getCallArgs(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php index b3b92a76e4a..888280cd346 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php @@ -1,4 +1,5 @@ isSingle()) { $atomics = $first_arg_type->getAtomicTypes(); $object_type = reset($atomics); - if ($object_type instanceof Type\Atomic\TObjectWithProperties) { + if ($object_type instanceof TObjectWithProperties) { if ([] === $object_type->properties) { return Type::getEmptyArray(); } - return new Type\Union([ - new Type\Atomic\TKeyedArray($object_type->properties) + return new Union([ + new TKeyedArray($object_type->properties) ]); } - if ($object_type instanceof Type\Atomic\TNamedObject) { + if ($object_type instanceof TNamedObject) { if (strtolower($object_type->value) === strtolower(stdClass::class)) { return Type::parseString('array'); } @@ -84,15 +89,15 @@ public static function getGetObjectVarsReturnType( return Type::getEmptyArray(); } - return new Type\Union([ - new Type\Atomic\TKeyedArray($properties) + return new Union([ + new TKeyedArray($properties) ]); } } return Type::parseString('array'); } - public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Type\Union + public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union { $statements_source = $event->getStatementsSource(); $call_args = $event->getCallArgs(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php index 7897527f74e..b6c6078813f 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php @@ -1,10 +1,12 @@ getStatementsSource(); if (!$statements_source instanceof StatementsAnalyzer) { diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php index 7617743a192..c97048aefd1 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php @@ -1,4 +1,5 @@ getSource(); $call_args = $event->getCallArgs(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php index 9d219c00950..519984f2f52 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php @@ -1,4 +1,5 @@ getCallArgs(); $bool = Type::getBool(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php index 994de0c1611..52d26475169 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -46,16 +53,16 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $atomic_types = $first_arg_type->getAtomicTypes(); while ($call_arg_atomic_type = array_shift($atomic_types)) { - if ($call_arg_atomic_type instanceof Type\Atomic\TTemplateParam) { + if ($call_arg_atomic_type instanceof TTemplateParam) { $atomic_types = array_merge($atomic_types, $call_arg_atomic_type->as->getAtomicTypes()); continue; } - if ($call_arg_atomic_type instanceof Type\Atomic\TNamedObject + if ($call_arg_atomic_type instanceof TNamedObject && AtomicTypeComparator::isContainedBy( $codebase, $call_arg_atomic_type, - new Type\Atomic\TIterable([Type::getMixed(), Type::getMixed()]) + new TIterable([Type::getMixed(), Type::getMixed()]) ) ) { $has_valid_iterator = true; @@ -80,8 +87,8 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($second_arg_type && ((string) $second_arg_type === 'false') ) { - return new Type\Union([ - new Type\Atomic\TList($value_type), + return new Union([ + new TList($value_type), ]); } @@ -100,12 +107,12 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $template_type = array_shift($template_types); if ($template_type->as->hasMixed()) { $template_type->as = Type::getArrayKey(); - $key_type = new Type\Union([$template_type]); + $key_type = new Union([$template_type]); } } - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ $key_type, $value_type, ]), diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php index 4f0e821316e..1a24e22b30b 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php @@ -1,4 +1,6 @@ -getCallArgs(); if (count($call_args) === 0) { @@ -58,16 +63,16 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev break; } - if ($atomic_type instanceof Type\Atomic\TLiteralInt) { + if ($atomic_type instanceof TLiteralInt) { $min_bounds[] = $atomic_type->value; $max_bounds[] = $atomic_type->value; - } elseif ($atomic_type instanceof Type\Atomic\TIntRange) { + } elseif ($atomic_type instanceof TIntRange) { $min_bounds[] = $atomic_type->min_bound; $max_bounds[] = $atomic_type->max_bound; - } elseif ($atomic_type instanceof Type\Atomic\TPositiveInt) { + } elseif ($atomic_type instanceof TPositiveInt) { $min_bounds[] = 1; $max_bounds[] = null; - } elseif (get_class($atomic_type) === Type\Atomic\TInt::class) { + } elseif (get_class($atomic_type) === TInt::class) { $min_bounds[] = null; $max_bounds[] = null; } else { @@ -108,7 +113,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return Type::getInt(false, $min_potential_int); } - return new Union([new Type\Atomic\TIntRange($min_potential_int, $max_potential_int)]); + return new Union([new TIntRange($min_potential_int, $max_potential_int)]); } //if we're dealing with non-int elements, just combine them all together @@ -117,10 +122,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($array_arg_type = $nodeTypeProvider->getType($arg->value)) { if ($array_arg_type->isSingle()) { $atomic_type = $array_arg_type->getSingleAtomic(); - if ($atomic_type instanceof Type\Atomic\TPositiveInt) { + if ($atomic_type instanceof TPositiveInt) { //we replace TPositiveInt with a range for better combination $array_arg_type->removeType('int'); - $array_arg_type->addType(new Type\Atomic\TIntRange(1, null)); + $array_arg_type->addType(new TIntRange(1, null)); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php index 0be42df41dd..4b9ea038957 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php @@ -1,10 +1,14 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -30,7 +34,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if (!($call_arg_type = $statements_source->node_data->getType($call_arg->value)) || !$call_arg_type->isInt() ) { - $value_type = new Type\Union([new Type\Atomic\TInt, new Type\Atomic\TFalse]); + $value_type = new Union([new TInt, new TFalse]); $codebase = $statements_source->getCodebase(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php index 6d98842a26c..8beea77c32d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -41,18 +49,18 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if (!$component_type->hasMixed()) { $codebase = $statements_source->getCodebase(); - $acceptable_string_component_type = new Type\Union([ - new Type\Atomic\TLiteralInt(PHP_URL_SCHEME), - new Type\Atomic\TLiteralInt(PHP_URL_USER), - new Type\Atomic\TLiteralInt(PHP_URL_PASS), - new Type\Atomic\TLiteralInt(PHP_URL_HOST), - new Type\Atomic\TLiteralInt(PHP_URL_PATH), - new Type\Atomic\TLiteralInt(PHP_URL_QUERY), - new Type\Atomic\TLiteralInt(PHP_URL_FRAGMENT), + $acceptable_string_component_type = new Union([ + new TLiteralInt(PHP_URL_SCHEME), + new TLiteralInt(PHP_URL_USER), + new TLiteralInt(PHP_URL_PASS), + new TLiteralInt(PHP_URL_HOST), + new TLiteralInt(PHP_URL_PATH), + new TLiteralInt(PHP_URL_QUERY), + new TLiteralInt(PHP_URL_FRAGMENT), ]); - $acceptable_int_component_type = new Type\Union([ - new Type\Atomic\TLiteralInt(PHP_URL_PORT), + $acceptable_int_component_type = new Union([ + new TLiteralInt(PHP_URL_PORT), ]); if (UnionTypeComparator::isContainedBy( @@ -60,10 +68,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $component_type, $acceptable_string_component_type )) { - $nullable_falsable_string = new Type\Union([ - new Type\Atomic\TString, - new Type\Atomic\TFalse, - new Type\Atomic\TNull, + $nullable_falsable_string = new Union([ + new TString, + new TFalse, + new TNull, ]); $codebase = $statements_source->getCodebase(); @@ -84,10 +92,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $component_type, $acceptable_int_component_type )) { - $nullable_falsable_int = new Type\Union([ - new Type\Atomic\TInt, - new Type\Atomic\TFalse, - new Type\Atomic\TNull, + $nullable_falsable_int = new Union([ + new TInt, + new TFalse, + new TNull, ]); $codebase = $statements_source->getCodebase(); @@ -105,10 +113,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } } - $nullable_string_or_int = new Type\Union([ - new Type\Atomic\TString, - new Type\Atomic\TInt, - new Type\Atomic\TNull, + $nullable_string_or_int = new Union([ + new TString, + new TInt, + new TNull, ]); $codebase = $statements_source->getCodebase(); @@ -135,9 +143,9 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $component_type->possibly_undefined = true; } - $return_type = new Type\Union([ - new Type\Atomic\TKeyedArray($component_types), - new Type\Atomic\TFalse(), + $return_type = new Union([ + new TKeyedArray($component_types), + new TFalse(), ]); if ($statements_source->getCodebase()->config->ignore_internal_falsable_issues) { diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php index 1f29ec1b534..86ee127178a 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php @@ -1,10 +1,19 @@ getSource(); $call_args = $event->getCallArgs(); @@ -30,73 +39,73 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event) switch ($fetch_mode) { case PDO::FETCH_ASSOC: // array|false - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ Type::getString(), - new Type\Union([ - new Type\Atomic\TScalar(), - new Type\Atomic\TNull() + new Union([ + new TScalar(), + new TNull() ]) ]), - new Type\Atomic\TFalse(), + new TFalse(), ]); case PDO::FETCH_BOTH: // array|false - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ Type::getArrayKey(), - new Type\Union([ - new Type\Atomic\TScalar(), - new Type\Atomic\TNull() + new Union([ + new TScalar(), + new TNull() ]) ]), - new Type\Atomic\TFalse(), + new TFalse(), ]); case PDO::FETCH_BOUND: // bool return Type::getBool(); case PDO::FETCH_CLASS: // object|false - return new Type\Union([ - new Type\Atomic\TObject(), - new Type\Atomic\TFalse(), + return new Union([ + new TObject(), + new TFalse(), ]); case PDO::FETCH_LAZY: // object|false // This actually returns a PDORow object, but that class is // undocumented, and its attributes are all dynamic anyway - return new Type\Union([ - new Type\Atomic\TObject(), - new Type\Atomic\TFalse(), + return new Union([ + new TObject(), + new TFalse(), ]); case PDO::FETCH_NAMED: // array>|false - return new Type\Union([ - new Type\Atomic\TArray([ + return new Union([ + new TArray([ Type::getString(), - new Type\Union([ - new Type\Atomic\TScalar(), - new Type\Atomic\TList(Type::getScalar()) + new Union([ + new TScalar(), + new TList(Type::getScalar()) ]) ]), - new Type\Atomic\TFalse(), + new TFalse(), ]); case PDO::FETCH_NUM: // list|false - return new Type\Union([ - new Type\Atomic\TList( - new Type\Union([ - new Type\Atomic\TScalar(), - new Type\Atomic\TNull() + return new Union([ + new TList( + new Union([ + new TScalar(), + new TNull() ]) ), - new Type\Atomic\TFalse(), + new TFalse(), ]); case PDO::FETCH_OBJ: // stdClass|false - return new Type\Union([ - new Type\Atomic\TNamedObject('stdClass'), - new Type\Atomic\TFalse(), + return new Union([ + new TNamedObject('stdClass'), + new TFalse(), ]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php index e059911f8c3..e352ffd48e8 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php @@ -1,4 +1,5 @@ getCallArgs(); if (count($call_args) === 0) { @@ -38,11 +44,11 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $min_value = null; if ($first_arg !== null && $first_arg->isSingle()) { $first_atomic_type = $first_arg->getSingleAtomic(); - if ($first_atomic_type instanceof Type\Atomic\TLiteralInt) { + if ($first_atomic_type instanceof TLiteralInt) { $min_value = $first_atomic_type->value; - } elseif ($first_atomic_type instanceof Type\Atomic\TIntRange) { + } elseif ($first_atomic_type instanceof TIntRange) { $min_value = $first_atomic_type->min_bound; - } elseif ($first_atomic_type instanceof Type\Atomic\TPositiveInt) { + } elseif ($first_atomic_type instanceof TPositiveInt) { $min_value = 1; } } @@ -50,15 +56,15 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $max_value = null; if ($second_arg !== null && $second_arg->isSingle()) { $second_atomic_type = $second_arg->getSingleAtomic(); - if ($second_atomic_type instanceof Type\Atomic\TLiteralInt) { + if ($second_atomic_type instanceof TLiteralInt) { $max_value = $second_atomic_type->value; - } elseif ($second_atomic_type instanceof Type\Atomic\TIntRange) { + } elseif ($second_atomic_type instanceof TIntRange) { $max_value = $second_atomic_type->max_bound; - } elseif ($second_atomic_type instanceof Type\Atomic\TPositiveInt) { + } elseif ($second_atomic_type instanceof TPositiveInt) { $max_value = null; } } - return new Type\Union([new Type\Atomic\TIntRange($min_value, $max_value)]); + return new Union([new TIntRange($min_value, $max_value)]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php index f1d43eee7d9..88e71c3840d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php @@ -1,9 +1,11 @@ getCallArgs(); $method_name_lowercase = $event->getMethodNameLowercase(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php index a89c2ede23f..d32da31e979 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php @@ -1,10 +1,13 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -44,7 +47,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $return_type = Type::getString(); if (in_array($function_id, ['preg_replace', 'preg_replace_callback'], true)) { - $return_type->addType(new Type\Atomic\TNull()); + $return_type->addType(new TNull()); $codebase = $statements_source->getCodebase(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php index 8cd95a26d8c..cb3b01958d8 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php index 568cd0dafa0..385c6b61b5d 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php @@ -1,11 +1,18 @@ -getStatementsSource()->getCodebase(); $config = $codebase->config; if ($config->trigger_error_exits === 'always') { - return new Type\Union([new Type\Atomic\TNever()]); + return new Union([new TNever()]); } if ($config->trigger_error_exits === 'never') { - return new Type\Union([new Type\Atomic\TTrue()]); + return new Union([new TTrue()]); } //default behaviour @@ -44,17 +51,17 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev ) { $return_types = []; foreach ($array_arg_type->getAtomicTypes() as $atomicType) { - if ($atomicType instanceof Type\Atomic\TLiteralInt) { + if ($atomicType instanceof TLiteralInt) { if (in_array($atomicType->value, [E_USER_WARNING, E_USER_DEPRECATED, E_USER_NOTICE], true)) { - $return_types[] = new Type\Atomic\TTrue(); + $return_types[] = new TTrue(); } elseif ($atomicType->value === E_USER_ERROR) { - $return_types[] = new Type\Atomic\TNever(); + $return_types[] = new TNever(); } else { // not recognized int literal. return false before PHP8, fatal error since - $return_types[] = new Type\Atomic\TFalse(); + $return_types[] = new TFalse(); } } else { - $return_types[] = new Type\Atomic\TBool(); + $return_types[] = new TBool(); } } @@ -62,6 +69,6 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } //default value is E_USER_NOTICE, so return true - return new Type\Union([new Type\Atomic\TTrue()]); + return new Union([new TTrue()]); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php index 78f63533c5f..cab2b99971f 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php @@ -1,4 +1,5 @@ getStatementsSource(); $call_args = $event->getCallArgs(); @@ -32,21 +38,21 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if ($operator_type) { if (!$operator_type->hasMixed()) { - $acceptable_operator_type = new Type\Union([ - new Type\Atomic\TLiteralString('<'), - new Type\Atomic\TLiteralString('lt'), - new Type\Atomic\TLiteralString('<='), - new Type\Atomic\TLiteralString('le'), - new Type\Atomic\TLiteralString('>'), - new Type\Atomic\TLiteralString('gt'), - new Type\Atomic\TLiteralString('>='), - new Type\Atomic\TLiteralString('ge'), - new Type\Atomic\TLiteralString('=='), - new Type\Atomic\TLiteralString('='), - new Type\Atomic\TLiteralString('eq'), - new Type\Atomic\TLiteralString('!='), - new Type\Atomic\TLiteralString('<>'), - new Type\Atomic\TLiteralString('ne'), + $acceptable_operator_type = new Union([ + new TLiteralString('<'), + new TLiteralString('lt'), + new TLiteralString('<='), + new TLiteralString('le'), + new TLiteralString('>'), + new TLiteralString('gt'), + new TLiteralString('>='), + new TLiteralString('ge'), + new TLiteralString('=='), + new TLiteralString('='), + new TLiteralString('eq'), + new TLiteralString('!='), + new TLiteralString('<>'), + new TLiteralString('ne'), ]); $codebase = $statements_source->getCodebase(); @@ -61,16 +67,16 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } } - return new Type\Union([ - new Type\Atomic\TBool, - new Type\Atomic\TNull, + return new Union([ + new TBool, + new TNull, ]); } - return new Type\Union([ - new Type\Atomic\TLiteralInt(-1), - new Type\Atomic\TLiteralInt(0), - new Type\Atomic\TLiteralInt(1), + return new Union([ + new TLiteralInt(-1), + new TLiteralInt(0), + new TLiteralInt(1), ]); } } diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index f8c4624e340..3a5cb57fd80 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -1,4 +1,5 @@ type = clone $type; if ($this->type->getLiteralStrings()) { - $this->type->addType(new Type\Atomic\TString); + $this->type->addType(new TString); } if ($this->type->getLiteralInts()) { - $this->type->addType(new Type\Atomic\TInt); + $this->type->addType(new TInt); } if ($this->type->getLiteralFloats()) { - $this->type->addType(new Type\Atomic\TFloat); + $this->type->addType(new TFloat); } } } diff --git a/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php b/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php index f1bba7d1684..b91edd73e9a 100644 --- a/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php +++ b/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php @@ -1,4 +1,5 @@ value->name instanceof PhpParser\Node\Identifier && strtolower($array_item->value->name->name) ) { - $map[$array_item->key->value] = new Type\Union([ - new Type\Atomic\TNamedObject(implode('\\', $array_item->value->class->parts)) + $map[$array_item->key->value] = new Union([ + new TNamedObject(implode('\\', $array_item->value->class->parts)) ]); } elseif ($array_item->value instanceof PhpParser\Node\Scalar\String_) { $map[$array_item->key->value] = $array_item->value->value; @@ -105,7 +111,7 @@ function ( $offset, $meta_fq_classlike_name, $meta_method_name - ): ?Type\Union { + ): ?Union { $statements_analyzer = $event->getSource(); $call_args = $event->getCallArgs(); $method_name = $event->getMethodNameLowercase(); @@ -127,7 +133,7 @@ function ( $offset_arg_value = $call_arg_type->getSingleStringLiteral()->value; if ($mapped_type = $map[$offset_arg_value] ?? null) { - if ($mapped_type instanceof Type\Union) { + if ($mapped_type instanceof Union) { return clone $mapped_type; } } @@ -137,8 +143,8 @@ function ( $mapped_type = str_replace('@', $offset_arg_value, $mapped_type); if (strpos($mapped_type, '.') === false) { - return new Type\Union([ - new Type\Atomic\TNamedObject($mapped_type) + return new Union([ + new TNamedObject($mapped_type) ]); } } @@ -160,7 +166,7 @@ function ( $type_offset, $meta_fq_classlike_name, $meta_method_name - ): ?Type\Union { + ): ?Union { $statements_analyzer = $event->getSource(); $call_args = $event->getCallArgs(); $method_name = $event->getMethodNameLowercase(); @@ -197,7 +203,7 @@ function ( $element_type_offset, $meta_fq_classlike_name, $meta_method_name - ): ?Type\Union { + ): ?Union { $statements_analyzer = $event->getSource(); $call_args = $event->getCallArgs(); $method_name = $event->getMethodNameLowercase(); @@ -219,15 +225,15 @@ function ( ) { /** * @psalm-suppress PossiblyUndefinedStringArrayOffset - * @var Type\Atomic\TArray|Type\Atomic\TKeyedArray|Type\Atomic\TList + * @var TArray|TKeyedArray|TList */ $array_atomic_type = $call_arg_type->getAtomicTypes()['array']; - if ($array_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($array_atomic_type instanceof TKeyedArray) { return $array_atomic_type->getGenericValueType(); } - if ($array_atomic_type instanceof Type\Atomic\TList) { + if ($array_atomic_type instanceof TList) { return $array_atomic_type->type_param; } @@ -261,7 +267,7 @@ function ( ) use ( $map, $offset - ): Type\Union { + ): Union { $statements_analyzer = $event->getStatementsSource(); $call_args = $event->getCallArgs(); $function_id = $event->getFunctionId(); @@ -277,7 +283,7 @@ function ( $offset_arg_value = $call_arg_type->getSingleStringLiteral()->value; if ($mapped_type = $map[$offset_arg_value] ?? null) { - if ($mapped_type instanceof Type\Union) { + if ($mapped_type instanceof Union) { return clone $mapped_type; } } @@ -287,8 +293,8 @@ function ( $mapped_type = str_replace('@', $offset_arg_value, $mapped_type); if (strpos($mapped_type, '.') === false) { - return new Type\Union([ - new Type\Atomic\TNamedObject($mapped_type) + return new Union([ + new TNamedObject($mapped_type) ]); } } @@ -314,7 +320,7 @@ function ( FunctionReturnTypeProviderEvent $event ) use ( $type_offset - ): Type\Union { + ): Union { $statements_analyzer = $event->getStatementsSource(); $call_args = $event->getCallArgs(); $function_id = $event->getFunctionId(); @@ -348,7 +354,7 @@ function ( FunctionReturnTypeProviderEvent $event ) use ( $element_type_offset - ): Type\Union { + ): Union { $statements_analyzer = $event->getStatementsSource(); $call_args = $event->getCallArgs(); $function_id = $event->getFunctionId(); @@ -363,15 +369,15 @@ function ( ) { /** * @psalm-suppress PossiblyUndefinedStringArrayOffset - * @var Type\Atomic\TArray|Type\Atomic\TKeyedArray|Type\Atomic\TList + * @var TArray|TKeyedArray|TList */ $array_atomic_type = $call_arg_type->getAtomicTypes()['array']; - if ($array_atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($array_atomic_type instanceof TKeyedArray) { return $array_atomic_type->getGenericValueType(); } - if ($array_atomic_type instanceof Type\Atomic\TList) { + if ($array_atomic_type instanceof TList) { return $array_atomic_type->type_param; } diff --git a/src/Psalm/Internal/Scanner/VarDocblockComment.php b/src/Psalm/Internal/Scanner/VarDocblockComment.php index 330848f7c8c..7c38a339be1 100644 --- a/src/Psalm/Internal/Scanner/VarDocblockComment.php +++ b/src/Psalm/Internal/Scanner/VarDocblockComment.php @@ -1,7 +1,8 @@ |null + * @var array|null */ public $break_vars; diff --git a/src/Psalm/Internal/Scope/FinallyScope.php b/src/Psalm/Internal/Scope/FinallyScope.php index fc8e7cd8750..0343f193002 100644 --- a/src/Psalm/Internal/Scope/FinallyScope.php +++ b/src/Psalm/Internal/Scope/FinallyScope.php @@ -1,7 +1,8 @@ + * @var array */ public $vars_in_scope = []; /** - * @param array $vars_in_scope + * @param array $vars_in_scope */ public function __construct(array $vars_in_scope) { diff --git a/src/Psalm/Internal/Scope/IfConditionalScope.php b/src/Psalm/Internal/Scope/IfConditionalScope.php index 22c16881400..1f98135a08a 100644 --- a/src/Psalm/Internal/Scope/IfConditionalScope.php +++ b/src/Psalm/Internal/Scope/IfConditionalScope.php @@ -1,4 +1,5 @@ |null + * @var array|null */ public $new_vars; @@ -21,7 +22,7 @@ class IfScope public $new_vars_possibly_in_scope = []; /** - * @var array|null + * @var array|null */ public $redefined_vars; @@ -36,7 +37,7 @@ class IfScope public $possibly_assigned_var_ids = []; /** - * @var array + * @var array */ public $possibly_redefined_vars = []; diff --git a/src/Psalm/Internal/Scope/LoopScope.php b/src/Psalm/Internal/Scope/LoopScope.php index 1e620410013..4bbb2089c0f 100644 --- a/src/Psalm/Internal/Scope/LoopScope.php +++ b/src/Psalm/Internal/Scope/LoopScope.php @@ -1,8 +1,9 @@ |null + * @var array|null */ public $redefined_loop_vars = []; /** - * @var array + * @var array */ public $possibly_redefined_loop_vars = []; /** - * @var array|null + * @var array|null */ public $possibly_redefined_loop_parent_vars; /** - * @var array + * @var array */ public $possibly_defined_loop_parent_vars = []; diff --git a/src/Psalm/Internal/Scope/SwitchScope.php b/src/Psalm/Internal/Scope/SwitchScope.php index 0697d6fab5b..35bb99cae84 100644 --- a/src/Psalm/Internal/Scope/SwitchScope.php +++ b/src/Psalm/Internal/Scope/SwitchScope.php @@ -1,9 +1,10 @@ |null + * @var array|null */ public $new_vars_in_scope; @@ -21,12 +22,12 @@ class SwitchScope public $new_vars_possibly_in_scope = []; /** - * @var array|null + * @var array|null */ public $redefined_vars; /** - * @var array|null + * @var array|null */ public $possibly_redefined_vars; diff --git a/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php b/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php index 8a8105cbae5..20a7f4bb8a4 100644 --- a/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php @@ -16,6 +16,8 @@ use Psalm\Internal\Analyzer\ClassLikeAnalyzer; use Psalm\Internal\Scanner\ParsedDocblock; use Psalm\Type; +use Psalm\Type\Union; + use function array_slice; class ClassLikeStubGenerator @@ -116,7 +118,7 @@ private static function getConstantNodes(\Psalm\Codebase $codebase, ClassLikeSto foreach ($storage->constants as $constant_name => $constant_storage) { if ($constant_storage->unresolved_node) { - $type = new Type\Union([ + $type = new Union([ \Psalm\Internal\Codebase\ConstantTypeResolver::resolve( $codebase->classlikes, $constant_storage->unresolved_node diff --git a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php index 27ea916074f..d17b843d1c3 100644 --- a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php @@ -2,6 +2,80 @@ namespace Psalm\Internal\Stubs\Generator; +use Psalm\Type\Atomic\TAnonymousClassInstance; +use Psalm\Type\Atomic\TArray; +use Psalm\Type\Atomic\TArrayKey; +use Psalm\Type\Atomic\TAssertionFalsy; +use Psalm\Type\Atomic\TBool; +use Psalm\Type\Atomic\TCallable; +use Psalm\Type\Atomic\TCallableArray; +use Psalm\Type\Atomic\TCallableKeyedArray; +use Psalm\Type\Atomic\TCallableList; +use Psalm\Type\Atomic\TCallableObject; +use Psalm\Type\Atomic\TCallableString; +use Psalm\Type\Atomic\TClassConstant; +use Psalm\Type\Atomic\TClassString; +use Psalm\Type\Atomic\TClassStringMap; +use Psalm\Type\Atomic\TClosedResource; +use Psalm\Type\Atomic\TClosure; +use Psalm\Type\Atomic\TConditional; +use Psalm\Type\Atomic\TDependentGetClass; +use Psalm\Type\Atomic\TDependentGetDebugType; +use Psalm\Type\Atomic\TDependentGetType; +use Psalm\Type\Atomic\TDependentListKey; +use Psalm\Type\Atomic\TEmpty; +use Psalm\Type\Atomic\TEmptyMixed; +use Psalm\Type\Atomic\TEmptyNumeric; +use Psalm\Type\Atomic\TEmptyScalar; +use Psalm\Type\Atomic\TEnumCase; +use Psalm\Type\Atomic\TFalse; +use Psalm\Type\Atomic\TFloat; +use Psalm\Type\Atomic\TGenericObject; +use Psalm\Type\Atomic\TInt; +use Psalm\Type\Atomic\TIntMask; +use Psalm\Type\Atomic\TIntMaskOf; +use Psalm\Type\Atomic\TIntRange; +use Psalm\Type\Atomic\TIterable; +use Psalm\Type\Atomic\TKeyOfClassConstant; +use Psalm\Type\Atomic\TKeyedArray; +use Psalm\Type\Atomic\TList; +use Psalm\Type\Atomic\TLiteralClassString; +use Psalm\Type\Atomic\TLiteralFloat; +use Psalm\Type\Atomic\TLiteralInt; +use Psalm\Type\Atomic\TLiteralString; +use Psalm\Type\Atomic\TLowercaseString; +use Psalm\Type\Atomic\TMixed; +use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNever; +use Psalm\Type\Atomic\TNonEmptyArray; +use Psalm\Type\Atomic\TNonEmptyList; +use Psalm\Type\Atomic\TNonEmptyLowercaseString; +use Psalm\Type\Atomic\TNonEmptyMixed; +use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString; +use Psalm\Type\Atomic\TNonEmptyScalar; +use Psalm\Type\Atomic\TNonEmptyString; +use Psalm\Type\Atomic\TNonFalsyString; +use Psalm\Type\Atomic\TNonspecificLiteralInt; +use Psalm\Type\Atomic\TNonspecificLiteralString; +use Psalm\Type\Atomic\TNull; +use Psalm\Type\Atomic\TNumeric; +use Psalm\Type\Atomic\TNumericString; +use Psalm\Type\Atomic\TObject; +use Psalm\Type\Atomic\TObjectWithProperties; +use Psalm\Type\Atomic\TPositiveInt; +use Psalm\Type\Atomic\TResource; +use Psalm\Type\Atomic\TScalar; +use Psalm\Type\Atomic\TString; +use Psalm\Type\Atomic\TTemplateIndexedAccess; +use Psalm\Type\Atomic\TTemplateKeyOf; +use Psalm\Type\Atomic\TTemplateParam; +use Psalm\Type\Atomic\TTemplateParamClass; +use Psalm\Type\Atomic\TTraitString; +use Psalm\Type\Atomic\TTrue; +use Psalm\Type\Atomic\TTypeAlias; +use Psalm\Type\Atomic\TValueOfClassConstant; +use Psalm\Type\Atomic\TVoid; +use Psalm\Type\Atomic\Scalar; use PhpParser; use Psalm\Internal\Scanner\ParsedDocblock; use Psalm\Node\Expr\VirtualArray; @@ -22,6 +96,8 @@ use Psalm\Node\VirtualNullableType; use Psalm\Node\VirtualParam; use Psalm\Type; +use Psalm\Type\Union; + use function dirname; class StubsGenerator @@ -269,7 +345,7 @@ public static function getFunctionParamNodes(\Psalm\Storage\FunctionLikeStorage foreach ($method_storage->params as $param) { $param_nodes[] = new VirtualParam( new VirtualVariable($param->name), - $param->default_type instanceof Type\Union + $param->default_type instanceof Union ? self::getExpressionFromType($param->default_type) : null, $param->signature_type @@ -286,19 +362,19 @@ public static function getFunctionParamNodes(\Psalm\Storage\FunctionLikeStorage /** * @return PhpParser\Node\Identifier|PhpParser\Node\Name\FullyQualified|PhpParser\Node\NullableType|null */ - public static function getParserTypeFromPsalmType(Type\Union $type): ?PhpParser\NodeAbstract + public static function getParserTypeFromPsalmType(Union $type): ?PhpParser\NodeAbstract { $nullable = $type->isNullable(); foreach ($type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TNull) { + if ($atomic_type instanceof TNull) { continue; } - if ($atomic_type instanceof Type\Atomic\Scalar - || $atomic_type instanceof Type\Atomic\TObject - || $atomic_type instanceof Type\Atomic\TArray - || $atomic_type instanceof Type\Atomic\TIterable + if ($atomic_type instanceof Scalar + || $atomic_type instanceof TObject + || $atomic_type instanceof TArray + || $atomic_type instanceof TIterable ) { $identifier_string = $atomic_type->toPhpString(null, [], null, 8, 0); @@ -316,7 +392,7 @@ public static function getParserTypeFromPsalmType(Type\Union $type): ?PhpParser\ return $identifier; } - if ($atomic_type instanceof Type\Atomic\TNamedObject) { + if ($atomic_type instanceof TNamedObject) { $name_node = new VirtualFullyQualified($atomic_type->value); if ($nullable) { @@ -330,42 +406,42 @@ public static function getParserTypeFromPsalmType(Type\Union $type): ?PhpParser\ return null; } - public static function getExpressionFromType(Type\Union $type) : PhpParser\Node\Expr + public static function getExpressionFromType(Union $type) : PhpParser\Node\Expr { foreach ($type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TLiteralClassString) { + if ($atomic_type instanceof TLiteralClassString) { return new VirtualClassConstFetch(new VirtualName('\\' . $atomic_type->value), new VirtualIdentifier('class')); } - if ($atomic_type instanceof Type\Atomic\TLiteralString) { + if ($atomic_type instanceof TLiteralString) { return new VirtualString($atomic_type->value); } - if ($atomic_type instanceof Type\Atomic\TLiteralInt) { + if ($atomic_type instanceof TLiteralInt) { return new VirtualLNumber($atomic_type->value); } - if ($atomic_type instanceof Type\Atomic\TLiteralFloat) { + if ($atomic_type instanceof TLiteralFloat) { return new VirtualDNumber($atomic_type->value); } - if ($atomic_type instanceof Type\Atomic\TFalse) { + if ($atomic_type instanceof TFalse) { return new VirtualConstFetch(new VirtualName('false')); } - if ($atomic_type instanceof Type\Atomic\TTrue) { + if ($atomic_type instanceof TTrue) { return new VirtualConstFetch(new VirtualName('true')); } - if ($atomic_type instanceof Type\Atomic\TNull) { + if ($atomic_type instanceof TNull) { return new VirtualConstFetch(new VirtualName('null')); } - if ($atomic_type instanceof Type\Atomic\TArray) { + if ($atomic_type instanceof TArray) { return new VirtualArray([]); } - if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_type instanceof TKeyedArray) { $new_items = []; foreach ($atomic_type->properties as $property_name => $property_type) { @@ -386,7 +462,7 @@ public static function getExpressionFromType(Type\Union $type) : PhpParser\Node\ return new VirtualArray($new_items); } - if ($atomic_type instanceof Type\Atomic\TEnumCase) { + if ($atomic_type instanceof TEnumCase) { return new VirtualClassConstFetch(new VirtualName('\\' . $atomic_type->value), new VirtualIdentifier($atomic_type->case_name)); } } diff --git a/src/Psalm/Internal/Type/ArrayType.php b/src/Psalm/Internal/Type/ArrayType.php index 202996075a7..00238803ed9 100644 --- a/src/Psalm/Internal/Type/ArrayType.php +++ b/src/Psalm/Internal/Type/ArrayType.php @@ -1,33 +1,40 @@ -key = $key; $this->value = $value; $this->is_list = $is_list; } - public static function infer(Type\Atomic $type): ?self + public static function infer(Atomic $type): ?self { - if ($type instanceof Type\Atomic\TKeyedArray) { + if ($type instanceof TKeyedArray) { return new self( $type->getGenericKeyType(), $type->getGenericValueType(), @@ -35,7 +42,7 @@ public static function infer(Type\Atomic $type): ?self ); } - if ($type instanceof Type\Atomic\TList) { + if ($type instanceof TList) { return new self( Type::getInt(), $type->type_param, @@ -43,7 +50,7 @@ public static function infer(Type\Atomic $type): ?self ); } - if ($type instanceof Type\Atomic\TArray) { + if ($type instanceof TArray) { return new self( $type->type_params[0], $type->type_params[1], diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 11fec128ea9..c426694d959 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -1,4 +1,5 @@ Object * * @param string[] $suppressed_issues - * @param array> $template_type_map + * @param array> $template_type_map * @param-out 0|1|2 $failed_reconciliation */ public static function reconcile( @@ -239,7 +252,7 @@ public static function reconcile( } /** - * @param array> $template_type_map + * @param array> $template_type_map */ private static function getMissingType( string $assertion, @@ -287,7 +300,7 @@ private static function getMissingType( * * @param 0|1|2 $failed_reconciliation * @param string[] $suppressed_issues - * @param array> $template_type_map + * @param array> $template_type_map * @param-out 0|1|2 $failed_reconciliation */ private static function refine( @@ -357,7 +370,7 @@ private static function refine( } } - if ($new_type_part instanceof Type\Atomic\TTemplateParam + if ($new_type_part instanceof TTemplateParam && $new_type_part->as->isSingle() ) { $new_as_atomic = $new_type_part->as->getSingleAtomic(); @@ -382,17 +395,17 @@ private static function refine( } if ($acceptable_atomic_types) { - $new_type_part->as = new Type\Union($acceptable_atomic_types); + $new_type_part->as = new Union($acceptable_atomic_types); - return new Type\Union([$new_type_part]); + return new Union([$new_type_part]); } } - if ($new_type_part instanceof Type\Atomic\TKeyedArray) { + if ($new_type_part instanceof TKeyedArray) { $acceptable_atomic_types = []; foreach ($existing_var_type->getAtomicTypes() as $existing_var_type_part) { - if ($existing_var_type_part instanceof Type\Atomic\TKeyedArray) { + if ($existing_var_type_part instanceof TKeyedArray) { if (!array_intersect_key( $existing_var_type_part->properties, $new_type_part->properties @@ -409,7 +422,7 @@ private static function refine( } if ($acceptable_atomic_types) { - return new Type\Union($acceptable_atomic_types); + return new Union($acceptable_atomic_types); } } @@ -451,7 +464,7 @@ private static function refine( } if ($acceptable_atomic_types) { - return new Type\Union($acceptable_atomic_types); + return new Union($acceptable_atomic_types); } } elseif (!$new_type->hasMixed()) { $has_match = true; @@ -586,19 +599,19 @@ private static function refine( * This method receives two types. The goal is to use datas in the new type to reduce the existing_type to a more * precise version. For example: new is `array` old is `list` so the result is `list` * - * @param array> $template_type_map + * @param array> $template_type_map * * @psalm-suppress ComplexMethod we'd probably want to extract specific handling blocks at the end and also allow * early return once a specific case has been handled */ private static function filterTypeWithAnother( Codebase $codebase, - Type\Union $existing_type, - Type\Union $new_type, + Union $existing_type, + Union $new_type, array $template_type_map, bool &$has_match = false, bool &$any_scalar_type_match_found = false - ): Type\Union { + ): Union { $matching_atomic_types = []; $has_cloned_type = false; @@ -609,8 +622,8 @@ private static function filterTypeWithAnother( foreach ($existing_type->getAtomicTypes() as $key => $existing_type_part) { // special workaround because PHP allows floats to contain ints, but we don’t want this // behaviour here - if ($existing_type_part instanceof Type\Atomic\TFloat - && $new_type_part instanceof Type\Atomic\TInt + if ($existing_type_part instanceof TFloat + && $new_type_part instanceof TInt ) { $any_scalar_type_match_found = true; continue; @@ -635,23 +648,23 @@ private static function filterTypeWithAnother( $has_local_match = true; if ($atomic_comparison_results->type_coerced - && get_class($new_type_part) === Type\Atomic\TNamedObject::class - && $existing_type_part instanceof Type\Atomic\TGenericObject + && get_class($new_type_part) === TNamedObject::class + && $existing_type_part instanceof TGenericObject ) { // this is a hack - it's not actually rigorous, as the params may be different - $matching_atomic_types[] = new Type\Atomic\TGenericObject( + $matching_atomic_types[] = new TGenericObject( $new_type_part->value, $existing_type_part->type_params ); - } elseif ($new_type_part instanceof Type\Atomic\TNamedObject - && $existing_type_part instanceof Type\Atomic\TTemplateParam + } elseif ($new_type_part instanceof TNamedObject + && $existing_type_part instanceof TTemplateParam && $existing_type_part->as->hasObjectType() ) { $existing_type_part = clone $existing_type_part; $existing_type_part->as = self::filterTypeWithAnother( $codebase, $existing_type_part->as, - new Type\Union([$new_type_part]), + new Union([$new_type_part]), $template_type_map ); @@ -677,8 +690,8 @@ private static function filterTypeWithAnother( continue; } - if ($existing_type_part instanceof Type\Atomic\TNamedObject - && $new_type_part instanceof Type\Atomic\TNamedObject + if ($existing_type_part instanceof TNamedObject + && $new_type_part instanceof TNamedObject && ($codebase->interfaceExists($existing_type_part->value) || $codebase->interfaceExists($new_type_part->value)) ) { @@ -690,8 +703,8 @@ private static function filterTypeWithAnother( continue; } - if ($new_type_part instanceof Type\Atomic\TKeyedArray - && $existing_type_part instanceof Type\Atomic\TList + if ($new_type_part instanceof TKeyedArray + && $existing_type_part instanceof TList ) { $new_type_key = $new_type_part->getGenericKeyType(); $new_type_value = $new_type_part->getGenericValueType(); @@ -708,7 +721,7 @@ private static function filterTypeWithAnother( $any_scalar_type_match_found ); - $hybrid_type_part = new Type\Atomic\TKeyedArray($new_type_part->properties); + $hybrid_type_part = new TKeyedArray($new_type_part->properties); $hybrid_type_part->previous_key_type = Type::getInt(); $hybrid_type_part->previous_value_type = $new_type_value; $hybrid_type_part->is_list = true; @@ -727,8 +740,8 @@ private static function filterTypeWithAnother( } } - if ($new_type_part instanceof Type\Atomic\TTemplateParam - && $existing_type_part instanceof Type\Atomic\TTemplateParam + if ($new_type_part instanceof TTemplateParam + && $existing_type_part instanceof TTemplateParam && $new_type_part->param_name !== $existing_type_part->param_name && $new_type_part->as->hasObject() && $existing_type_part->as->hasObject() @@ -743,12 +756,12 @@ private static function filterTypeWithAnother( } //we filter both types of standard iterables - if (($new_type_part instanceof Type\Atomic\TGenericObject - || $new_type_part instanceof Type\Atomic\TArray - || $new_type_part instanceof Type\Atomic\TIterable) - && ($existing_type_part instanceof Type\Atomic\TGenericObject - || $existing_type_part instanceof Type\Atomic\TArray - || $existing_type_part instanceof Type\Atomic\TIterable) + if (($new_type_part instanceof TGenericObject + || $new_type_part instanceof TArray + || $new_type_part instanceof TIterable) + && ($existing_type_part instanceof TGenericObject + || $existing_type_part instanceof TArray + || $existing_type_part instanceof TIterable) && count($new_type_part->type_params) === count($existing_type_part->type_params) ) { $has_any_param_match = false; @@ -799,9 +812,9 @@ private static function filterTypeWithAnother( } //we filter the second part of a list with the second part of standard iterables - if (($new_type_part instanceof Type\Atomic\TArray - || $new_type_part instanceof Type\Atomic\TIterable) - && $existing_type_part instanceof Type\Atomic\TList + if (($new_type_part instanceof TArray + || $new_type_part instanceof TIterable) + && $existing_type_part instanceof TList ) { $has_any_param_match = false; @@ -847,9 +860,9 @@ private static function filterTypeWithAnother( } //we filter each property of a Keyed Array with the second part of standard iterables - if (($new_type_part instanceof Type\Atomic\TArray - || $new_type_part instanceof Type\Atomic\TIterable) - && $existing_type_part instanceof Type\Atomic\TKeyedArray + if (($new_type_part instanceof TArray + || $new_type_part instanceof TIterable) + && $existing_type_part instanceof TKeyedArray ) { $has_any_param_match = false; @@ -896,20 +909,20 @@ private static function filterTypeWithAnother( //These partial match wouldn't have been handled by AtomicTypeComparator $new_range = null; - if ($new_type_part instanceof Atomic\TIntRange && $existing_type_part instanceof Atomic\TPositiveInt) { + if ($new_type_part instanceof TIntRange && $existing_type_part instanceof TPositiveInt) { $new_range = TIntRange::intersectIntRanges( TIntRange::convertToIntRange($existing_type_part), $new_type_part ); - } elseif ($existing_type_part instanceof Atomic\TIntRange - && $new_type_part instanceof Atomic\TPositiveInt + } elseif ($existing_type_part instanceof TIntRange + && $new_type_part instanceof TPositiveInt ) { $new_range = TIntRange::intersectIntRanges( $existing_type_part, TIntRange::convertToIntRange($new_type_part) ); - } elseif ($new_type_part instanceof Atomic\TIntRange - && $existing_type_part instanceof Atomic\TIntRange + } elseif ($new_type_part instanceof TIntRange + && $existing_type_part instanceof TIntRange ) { $new_range = TIntRange::intersectIntRanges( $existing_type_part, @@ -938,7 +951,7 @@ private static function filterTypeWithAnother( } if ($matching_atomic_types) { - return new Type\Union($matching_atomic_types); + return new Union($matching_atomic_types); } return $new_type; @@ -951,13 +964,13 @@ private static function handleLiteralEquality( string $assertion, int $bracket_pos, bool $is_loose_equality, - Type\Union $existing_var_type, + Union $existing_var_type, string $old_var_type_string, ?string $var_id, bool $negated, ?CodeLocation $code_location, array $suppressed_issues - ): Type\Union { + ): Union { $value = substr($assertion, $bracket_pos + 1, -1); $scalar_type = substr($assertion, 0, $bracket_pos); @@ -976,7 +989,7 @@ private static function handleLiteralEquality( return $existing_var_type; } - return new Type\Union([new Type\Atomic\TLiteralInt($value)]); + return new Union([new TLiteralInt($value)]); } $has_int = false; @@ -994,7 +1007,7 @@ private static function handleLiteralEquality( return $existing_var_type; } - return new Type\Union([new Type\Atomic\TLiteralInt($value)]); + return new Union([new TLiteralInt($value)]); } if ($existing_var_atomic_type->as->hasInt()) { @@ -1012,7 +1025,7 @@ private static function handleLiteralEquality( foreach ($existing_var_atomic_types as $atomic_key => $atomic_type) { if ($atomic_key !== $assertion - && !($atomic_type instanceof Type\Atomic\TPositiveInt && $value > 0) + && !($atomic_type instanceof TPositiveInt && $value > 0) ) { $existing_var_type->removeType($atomic_key); $did_remove_type = true; @@ -1037,7 +1050,7 @@ private static function handleLiteralEquality( ); } } else { - $existing_var_type = new Type\Union([new Type\Atomic\TLiteralInt($value)]); + $existing_var_type = new Union([new TLiteralInt($value)]); } } elseif ($var_id && $code_location && !$is_loose_equality) { self::triggerIssueForImpossible( @@ -1105,10 +1118,10 @@ private static function handleLiteralEquality( || $scalar_type === 'interface-string' || $scalar_type === 'trait-string' ) { - return new Type\Union([new Type\Atomic\TLiteralClassString($value)]); + return new Union([new TLiteralClassString($value)]); } - return new Type\Union([new Type\Atomic\TLiteralString($value)]); + return new Union([new TLiteralString($value)]); } $has_string = false; @@ -1140,7 +1153,7 @@ private static function handleLiteralEquality( $suppressed_issues ); - return new Type\Union([$existing_var_atomic_type]); + return new Union([$existing_var_atomic_type]); } if ($existing_var_atomic_type->as->hasString()) { @@ -1185,9 +1198,9 @@ private static function handleLiteralEquality( || $scalar_type === 'interface-string' || $scalar_type === 'trait-string' ) { - $existing_var_type = new Type\Union([new Type\Atomic\TLiteralClassString($value)]); + $existing_var_type = new Union([new TLiteralClassString($value)]); } else { - $existing_var_type = new Type\Union([new Type\Atomic\TLiteralString($value)]); + $existing_var_type = new Union([new TLiteralString($value)]); } } } elseif ($var_id && $code_location && !$is_loose_equality) { @@ -1210,7 +1223,7 @@ private static function handleLiteralEquality( return $existing_var_type; } - return new Type\Union([new Type\Atomic\TLiteralFloat($value)]); + return new Union([new TLiteralFloat($value)]); } if ($existing_var_type->hasFloat()) { @@ -1245,7 +1258,7 @@ private static function handleLiteralEquality( ); } } else { - $existing_var_type = new Type\Union([new Type\Atomic\TLiteralFloat($value)]); + $existing_var_type = new Union([new TLiteralFloat($value)]); } } elseif ($var_id && $code_location && !$is_loose_equality) { self::triggerIssueForImpossible( @@ -1303,20 +1316,20 @@ private static function handleLiteralEquality( return $existing_var_type; } - return new Type\Union([new Type\Atomic\TEnumCase($fq_enum_name, $case_name)]); + return new Union([new TEnumCase($fq_enum_name, $case_name)]); } $can_be_equal = false; $did_remove_type = false; foreach ($existing_var_atomic_types as $atomic_key => $atomic_type) { - if (get_class($atomic_type) === Type\Atomic\TNamedObject::class + if (get_class($atomic_type) === TNamedObject::class && $atomic_type->value === $fq_enum_name ) { $can_be_equal = true; $did_remove_type = true; $existing_var_type->removeType($atomic_key); - $existing_var_type->addType(new Type\Atomic\TEnumCase($fq_enum_name, $case_name)); + $existing_var_type->addType(new TEnumCase($fq_enum_name, $case_name)); } elseif ($atomic_key !== $assertion) { $existing_var_type->removeType($atomic_key); $did_remove_type = true; @@ -1346,7 +1359,7 @@ private static function handleLiteralEquality( } /** - * @param array> $template_type_map + * @param array> $template_type_map * @param array $suppressed_issues */ private static function handleIsA( @@ -1369,15 +1382,15 @@ private static function handleIsA( } if ($existing_var_type->hasMixed()) { - $type = new Type\Union([ - new Type\Atomic\TNamedObject($assertion), + $type = new Union([ + new TNamedObject($assertion), ]); if ($allow_string_comparison) { $type->addType( - new Type\Atomic\TClassString( + new TClassString( $assertion, - new Type\Atomic\TNamedObject($assertion) + new TNamedObject($assertion) ) ); } @@ -1489,7 +1502,7 @@ private static function handleIsA( if (count($acceptable_atomic_types) === 1) { $should_return = true; - return new Type\Union([ + return new Union([ new TClassString('object', $acceptable_atomic_types[0]), ]); } diff --git a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php index 12a2de22540..d1b31b89578 100644 --- a/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php @@ -4,14 +4,17 @@ use Psalm\Codebase; use Psalm\Type; +use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TClassStringMap; +use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNonEmptyList; +use Psalm\Type\Union; use function array_map; use function range; @@ -27,25 +30,25 @@ class ArrayTypeComparator */ public static function isContainedBy( Codebase $codebase, - Type\Atomic $input_type_part, - Type\Atomic $container_type_part, + Atomic $input_type_part, + Atomic $container_type_part, bool $allow_interface_equality, ?TypeComparisonResult $atomic_comparison_result ): bool { $all_types_contain = true; $is_empty_array = $input_type_part->equals( - new Type\Atomic\TArray([ - new Type\Union([new Type\Atomic\TEmpty()]), - new Type\Union([new Type\Atomic\TEmpty()]) + new TArray([ + new Union([new TEmpty()]), + new Union([new TEmpty()]) ]), false ); if ($is_empty_array - && (($container_type_part instanceof Type\Atomic\TArray - && !$container_type_part instanceof Type\Atomic\TNonEmptyArray) - || ($container_type_part instanceof Type\Atomic\TKeyedArray + && (($container_type_part instanceof TArray + && !$container_type_part instanceof TNonEmptyArray) + || ($container_type_part instanceof TKeyedArray && !$container_type_part->isNonEmpty()) ) ) { @@ -190,13 +193,13 @@ public static function isContainedBy( if ($input_type_part->count !== null && $input_type_part->count < 10) { $literal_ints = array_map( function ($i) { - return new Type\Atomic\TLiteralInt($i); + return new TLiteralInt($i); }, range(0, $input_type_part->count - 1) ); $input_type_part = new TNonEmptyArray([ - new Type\Union($literal_ints), + new Union($literal_ints), clone $input_type_part->type_param ]); } else { @@ -223,7 +226,7 @@ function ($i) { } if ($input_param->isEmpty() - && $container_type_part instanceof Type\Atomic\TNonEmptyArray + && $container_type_part instanceof TNonEmptyArray ) { return false; } @@ -275,8 +278,8 @@ function ($i) { } } - if ($container_type_part instanceof Type\Atomic\TNonEmptyArray - && !$input_type_part instanceof Type\Atomic\TNonEmptyArray + if ($container_type_part instanceof TNonEmptyArray + && !$input_type_part instanceof TNonEmptyArray ) { if ($all_types_contain && $atomic_comparison_result) { $atomic_comparison_result->type_coerced = true; diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index 8b658ff75c2..109923c6182 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -5,13 +5,17 @@ use Psalm\Codebase; use Psalm\Internal\MethodIdentifier; use Psalm\Type; +use Psalm\Type\Atomic; use Psalm\Type\Atomic\Scalar; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TCallable; +use Psalm\Type\Atomic\TCallableKeyedArray; use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TCallableString; use Psalm\Type\Atomic\TClassStringMap; +use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TConditional; +use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEnumCase; use Psalm\Type\Atomic\TGenericObject; @@ -22,6 +26,8 @@ use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNever; +use Psalm\Type\Atomic\TNonEmptyArray; +use Psalm\Type\Atomic\TNonEmptyList; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TObject; use Psalm\Type\Atomic\TObjectWithProperties; @@ -45,8 +51,8 @@ class AtomicTypeComparator */ public static function isContainedBy( Codebase $codebase, - Type\Atomic $input_type_part, - Type\Atomic $container_type_part, + Atomic $input_type_part, + Atomic $container_type_part, bool $allow_interface_equality = false, bool $allow_float_int_equality = true, ?TypeComparisonResult $atomic_comparison_result = null @@ -88,7 +94,7 @@ public static function isContainedBy( return true; } - if ($input_type_part instanceof TNever || $input_type_part instanceof Type\Atomic\TEmpty) { + if ($input_type_part instanceof TNever || $input_type_part instanceof TEmpty) { return true; } @@ -134,7 +140,7 @@ public static function isContainedBy( ); } - if ($input_type_part instanceof Type\Atomic\TCallableKeyedArray + if ($input_type_part instanceof TCallableKeyedArray && $container_type_part instanceof TArray ) { return ArrayTypeComparator::isContainedBy( @@ -146,10 +152,10 @@ public static function isContainedBy( ); } - if (($container_type_part instanceof Type\Atomic\TCallable - && $input_type_part instanceof Type\Atomic\TCallable) - || ($container_type_part instanceof Type\Atomic\TClosure - && $input_type_part instanceof Type\Atomic\TClosure) + if (($container_type_part instanceof TCallable + && $input_type_part instanceof TCallable) + || ($container_type_part instanceof TClosure + && $input_type_part instanceof TClosure) ) { return CallableTypeComparator::isContainedBy( $codebase, @@ -159,43 +165,27 @@ public static function isContainedBy( ); } - if ($container_type_part instanceof Type\Atomic\TClosure && $input_type_part instanceof TCallable) { - if (CallableTypeComparator::isContainedBy( - $codebase, - $input_type_part, - $container_type_part, - $atomic_comparison_result - ) === false - ) { - return false; - } - - if ($atomic_comparison_result) { - $atomic_comparison_result->type_coerced = true; - } - - return false; - } + if ($container_type_part instanceof TClosure) { + if ($input_type_part instanceof TCallable) { + if (CallableTypeComparator::isContainedBy( + $codebase, + $input_type_part, + $container_type_part, + $atomic_comparison_result + ) === false + ) { + return false; + } - if ($container_type_part instanceof Type\Atomic\TClosure) { - if (!$input_type_part instanceof Type\Atomic\TClosure) { if ($atomic_comparison_result) { $atomic_comparison_result->type_coerced = true; - $atomic_comparison_result->type_coerced_from_mixed = true; } - - return false; } - return CallableTypeComparator::isContainedBy( - $codebase, - $input_type_part, - $container_type_part, - $atomic_comparison_result - ); + return false; } - if ($container_type_part instanceof TCallable && $input_type_part instanceof Type\Atomic\TClosure) { + if ($container_type_part instanceof TCallable && $input_type_part instanceof TClosure) { return CallableTypeComparator::isContainedBy( $codebase, $input_type_part, @@ -217,7 +207,7 @@ public static function isContainedBy( return true; } - if ($input_type_part instanceof Type\Atomic\TCallableObject && + if ($input_type_part instanceof TCallableObject && $container_type_part instanceof TObject ) { return true; @@ -665,14 +655,14 @@ public static function isContainedBy( */ public static function canBeIdentical( Codebase $codebase, - Type\Atomic $type1_part, - Type\Atomic $type2_part, + Atomic $type1_part, + Atomic $type2_part, bool $allow_interface_equality = true ): bool { if ((get_class($type1_part) === TList::class - && $type2_part instanceof Type\Atomic\TNonEmptyList) + && $type2_part instanceof TNonEmptyList) || (get_class($type2_part) === TList::class - && $type1_part instanceof Type\Atomic\TNonEmptyList) + && $type1_part instanceof TNonEmptyList) ) { return UnionTypeComparator::canExpressionTypesBeIdentical( $codebase, @@ -682,9 +672,9 @@ public static function canBeIdentical( } if ((get_class($type1_part) === TArray::class - && $type2_part instanceof Type\Atomic\TNonEmptyArray) + && $type2_part instanceof TNonEmptyArray) || (get_class($type2_part) === TArray::class - && $type1_part instanceof Type\Atomic\TNonEmptyArray) + && $type1_part instanceof TNonEmptyArray) ) { return UnionTypeComparator::canExpressionTypesBeIdentical( $codebase, diff --git a/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php b/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php index c86f81ceb03..107e3dd5785 100644 --- a/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php @@ -15,12 +15,15 @@ use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TCallable; +use Psalm\Type\Atomic\TCallableArray; +use Psalm\Type\Atomic\TCallableList; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TTemplateParam; use UnexpectedValueException; use function end; @@ -33,13 +36,13 @@ class CallableTypeComparator { /** - * @param TCallable|Type\Atomic\TClosure $input_type_part - * @param TCallable|Type\Atomic\TClosure $container_type_part + * @param TCallable|TClosure $input_type_part + * @param TCallable|TClosure $container_type_part */ public static function isContainedBy( Codebase $codebase, - Type\Atomic $input_type_part, - Type\Atomic $container_type_part, + Atomic $input_type_part, + Atomic $container_type_part, ?TypeComparisonResult $atomic_comparison_result ): bool { if ($container_type_part->is_pure && !$input_type_part->is_pure) { @@ -132,7 +135,7 @@ public static function isContainedBy( public static function isNotExplicitlyCallableTypeCallable( Codebase $codebase, - Type\Atomic $input_type_part, + Atomic $input_type_part, TCallable $container_type_part, ?TypeComparisonResult $atomic_comparison_result ): bool { @@ -152,7 +155,7 @@ public static function isNotExplicitlyCallableTypeCallable( return false; } - if (!$input_type_part instanceof Type\Atomic\TCallableList) { + if (!$input_type_part instanceof TCallableList) { if ($atomic_comparison_result) { $atomic_comparison_result->type_coerced_from_mixed = true; $atomic_comparison_result->type_coerced = true; @@ -178,7 +181,7 @@ public static function isNotExplicitlyCallableTypeCallable( return false; } - if (!$input_type_part instanceof Type\Atomic\TCallableArray) { + if (!$input_type_part instanceof TCallableArray) { if ($atomic_comparison_result) { $atomic_comparison_result->type_coerced_from_mixed = true; $atomic_comparison_result->type_coerced = true; @@ -232,7 +235,7 @@ public static function isNotExplicitlyCallableTypeCallable( */ public static function getCallableFromAtomic( Codebase $codebase, - Type\Atomic $input_type_part, + Atomic $input_type_part, ?TCallable $container_type_part = null, ?StatementsAnalyzer $statements_analyzer = null, bool $expand_callable = false @@ -439,7 +442,7 @@ public static function getCallableMethodIdFromTKeyedArray( strtolower($lhs_atomic_type->value) . '::', $calling_method_id ?: $file_name ); - } elseif ($lhs_atomic_type instanceof Atomic\TTemplateParam) { + } elseif ($lhs_atomic_type instanceof TTemplateParam) { $lhs_template_type = $lhs_atomic_type->as; if ($lhs_template_type->isSingle()) { $lhs_template_atomic_type = $lhs_template_type->getSingleAtomic(); @@ -477,7 +480,7 @@ public static function getCallableMethodIdFromTKeyedArray( foreach ($lhs->getAtomicTypes() as $lhs_atomic_type) { if ($lhs_atomic_type instanceof TNamedObject) { $class_name = $lhs_atomic_type->value; - } elseif ($lhs_atomic_type instanceof Atomic\TTemplateParam) { + } elseif ($lhs_atomic_type instanceof TTemplateParam) { $lhs_template_type = $lhs_atomic_type->as; if ($lhs_template_type->isSingle()) { $lhs_template_atomic_type = $lhs_template_type->getSingleAtomic(); @@ -487,7 +490,7 @@ public static function getCallableMethodIdFromTKeyedArray( $class_name = $lhs_template_atomic_type->as; } } - } elseif ($lhs_atomic_type instanceof Type\Atomic\TClassString + } elseif ($lhs_atomic_type instanceof TClassString && $lhs_atomic_type->as ) { $class_name = $lhs_atomic_type->as; diff --git a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php index 89efd338f66..dd7e648c3fd 100644 --- a/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php @@ -5,6 +5,7 @@ use Psalm\Codebase; use Psalm\Internal\Type\TemplateStandinTypeReplacer; use Psalm\Type; +use Psalm\Type\Atomic; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TNamedObject; @@ -23,8 +24,8 @@ class GenericTypeComparator */ public static function isContainedBy( Codebase $codebase, - Type\Atomic $input_type_part, - Type\Atomic $container_type_part, + Atomic $input_type_part, + Atomic $container_type_part, bool $allow_interface_equality = false, ?TypeComparisonResult $atomic_comparison_result = null ): bool { diff --git a/src/Psalm/Internal/Type/Comparator/IntegerRangeComparator.php b/src/Psalm/Internal/Type/Comparator/IntegerRangeComparator.php index 557e822961f..486a4df8c71 100644 --- a/src/Psalm/Internal/Type/Comparator/IntegerRangeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/IntegerRangeComparator.php @@ -5,6 +5,7 @@ use Psalm\Type\Atomic; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntRange; +use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TNonspecificLiteralInt; use Psalm\Type\Atomic\TPositiveInt; use Psalm\Type\Union; @@ -146,7 +147,7 @@ private static function reduceRangeIncrementally(array &$container_atomic_types, //the range in input is wider than container, we return false return false; } - } elseif ($container_atomic_type instanceof Atomic\TLiteralInt) { + } elseif ($container_atomic_type instanceof TLiteralInt) { if (!$reduced_range->contains($container_atomic_type->value)) { unset($container_atomic_types[$key]); //we don't need this one anymore } elseif ($reduced_range->min_bound === $container_atomic_type->value) { diff --git a/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php b/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php index 616ebd15146..bb42838b7e8 100644 --- a/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php +++ b/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php @@ -4,6 +4,7 @@ use Psalm\Codebase; use Psalm\Type; +use Psalm\Type\Atomic; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TObjectWithProperties; @@ -21,8 +22,8 @@ class KeyedArrayComparator */ public static function isContainedBy( Codebase $codebase, - Type\Atomic $input_type_part, - Type\Atomic $container_type_part, + Atomic $input_type_part, + Atomic $container_type_part, bool $allow_interface_equality, ?TypeComparisonResult $atomic_comparison_result ): bool { diff --git a/src/Psalm/Internal/Type/Comparator/ObjectComparator.php b/src/Psalm/Internal/Type/Comparator/ObjectComparator.php index dc5ac0d9549..b7b4ff301db 100644 --- a/src/Psalm/Internal/Type/Comparator/ObjectComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ObjectComparator.php @@ -4,7 +4,7 @@ use Psalm\Codebase; use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; -use Psalm\Type; +use Psalm\Type\Atomic; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNull; @@ -29,8 +29,8 @@ class ObjectComparator */ public static function isShallowlyContainedBy( Codebase $codebase, - Type\Atomic $input_type_part, - Type\Atomic $container_type_part, + Atomic $input_type_part, + Atomic $container_type_part, bool $allow_interface_equality, ?TypeComparisonResult $atomic_comparison_result ): bool { diff --git a/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php b/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php index af93ca84126..a790a715514 100644 --- a/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php @@ -4,7 +4,6 @@ use Psalm\Codebase; use Psalm\Internal\Analyzer\ClassLikeAnalyzer; -use Psalm\Type; use Psalm\Type\Atomic\Scalar; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; @@ -26,6 +25,7 @@ use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TLowercaseString; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNonEmptyLowercaseString; use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString; use Psalm\Type\Atomic\TNonEmptyString; use Psalm\Type\Atomic\TNonFalsyString; @@ -37,6 +37,7 @@ use Psalm\Type\Atomic\TScalar; use Psalm\Type\Atomic\TSingleLetter; use Psalm\Type\Atomic\TString; +use Psalm\Type\Atomic\TTemplateKeyOf; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Atomic\TTemplateParamClass; use Psalm\Type\Atomic\TTraitString; @@ -131,26 +132,26 @@ public static function isContainedBy( return true; } - if (($container_type_part instanceof Type\Atomic\TLowercaseString - || $container_type_part instanceof Type\Atomic\TNonEmptyLowercaseString) + if (($container_type_part instanceof TLowercaseString + || $container_type_part instanceof TNonEmptyLowercaseString) && $input_type_part instanceof TString ) { - if (($input_type_part instanceof Type\Atomic\TLowercaseString - && $container_type_part instanceof Type\Atomic\TLowercaseString) - || ($input_type_part instanceof Type\Atomic\TNonEmptyLowercaseString - && $container_type_part instanceof Type\Atomic\TNonEmptyLowercaseString) + if (($input_type_part instanceof TLowercaseString + && $container_type_part instanceof TLowercaseString) + || ($input_type_part instanceof TNonEmptyLowercaseString + && $container_type_part instanceof TNonEmptyLowercaseString) ) { return true; } - if ($input_type_part instanceof Type\Atomic\TNonEmptyLowercaseString - && $container_type_part instanceof Type\Atomic\TLowercaseString + if ($input_type_part instanceof TNonEmptyLowercaseString + && $container_type_part instanceof TLowercaseString ) { return true; } - if ($input_type_part instanceof Type\Atomic\TLowercaseString - && $container_type_part instanceof Type\Atomic\TNonEmptyLowercaseString + if ($input_type_part instanceof TLowercaseString + && $container_type_part instanceof TNonEmptyLowercaseString ) { if ($atomic_comparison_result) { $atomic_comparison_result->type_coerced = true; @@ -161,7 +162,7 @@ public static function isContainedBy( if ($input_type_part instanceof TLiteralString) { if (strtolower($input_type_part->value) === $input_type_part->value) { - return $input_type_part->value || $container_type_part instanceof Type\Atomic\TLowercaseString; + return $input_type_part->value || $container_type_part instanceof TLowercaseString; } return false; @@ -265,12 +266,12 @@ public static function isContainedBy( if ($container_type_part instanceof TArrayKey && ($input_type_part instanceof TInt || $input_type_part instanceof TString - || $input_type_part instanceof Type\Atomic\TTemplateKeyOf) + || $input_type_part instanceof TTemplateKeyOf) ) { return true; } - if ($input_type_part instanceof Type\Atomic\TTemplateKeyOf) { + if ($input_type_part instanceof TTemplateKeyOf) { foreach ($input_type_part->as->getAtomicTypes() as $atomic_type) { if ($atomic_type instanceof TArray) { /** @var Scalar $array_key_atomic */ @@ -477,8 +478,8 @@ public static function isContainedBy( return false; } - if (($input_type_part instanceof Type\Atomic\TLowercaseString - || $input_type_part instanceof Type\Atomic\TNonEmptyLowercaseString) + if (($input_type_part instanceof TLowercaseString + || $input_type_part instanceof TNonEmptyLowercaseString) && $container_type_part instanceof TLiteralString && strtolower($container_type_part->value) === $container_type_part->value ) { diff --git a/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php b/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php index c7a2cb848da..5fbe29af824 100644 --- a/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php +++ b/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php @@ -22,7 +22,17 @@ class TypeComparisonResult /** @var ?bool */ public $to_string_cast; - /** @var ?bool */ + /** + * This is primarily used for array access. + * For example in this function we know that there are only two possible keys, 0 and 1 + * But we allow the array to be addressed by an arbitrary integer $i. + * + * function takesAnInt(int $i): string { + * return ["foo", "bar"][$i]; + * } + * + * @var ?bool + */ public $type_coerced_from_scalar; /** @var ?Union */ diff --git a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php index 87c7f9ff9e6..b60483522c1 100644 --- a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php @@ -4,16 +4,18 @@ use Psalm\Codebase; use Psalm\Internal\Type\TypeExpander; -use Psalm\Type; use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArrayKey; +use Psalm\Type\Atomic\TClassConstant; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TNumeric; +use Psalm\Type\Atomic\TPositiveInt; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Atomic\TTypeAlias; +use Psalm\Type\Union; use function array_merge; use function array_pop; @@ -30,8 +32,8 @@ class UnionTypeComparator */ public static function isContainedBy( Codebase $codebase, - Type\Union $input_type, - Type\Union $container_type, + Union $input_type, + Union $container_type, bool $ignore_null = false, bool $ignore_false = false, ?TypeComparisonResult $union_comparison_result = null, @@ -82,7 +84,7 @@ public static function isContainedBy( continue; } - if ($input_type_part instanceof Type\Atomic\TClassConstant) { + if ($input_type_part instanceof TClassConstant) { $expanded = TypeExpander::expandAtomic( $codebase, $input_type_part, @@ -94,7 +96,7 @@ public static function isContainedBy( ); if ($expanded instanceof Atomic) { - if (!$expanded instanceof Atomic\TClassConstant) { + if (!$expanded instanceof TClassConstant) { $input_atomic_types[] = $expanded; continue; } @@ -129,7 +131,7 @@ public static function isContainedBy( } } - if ($input_type_part instanceof Atomic\TIntRange && $container_type->hasInt()) { + if ($input_type_part instanceof TIntRange && $container_type->hasInt()) { if (IntegerRangeComparator::isContainedByUnion( $input_type_part, $container_type @@ -318,8 +320,8 @@ public static function isContainedBy( * */ public static function isContainedByInPhp( - ?Type\Union $input_type, - Type\Union $container_type + ?Union $input_type, + Union $container_type ): bool { if ($container_type->isMixed()) { return true; @@ -363,8 +365,8 @@ public static function isContainedByInPhp( */ public static function canBeContainedBy( Codebase $codebase, - Type\Union $input_type, - Type\Union $container_type, + Union $input_type, + Union $container_type, bool $ignore_null = false, bool $ignore_false = false, array &$matching_input_keys = [] @@ -418,8 +420,8 @@ public static function canBeContainedBy( */ public static function canExpressionTypesBeIdentical( Codebase $codebase, - Type\Union $type1, - Type\Union $type2, + Union $type1, + Union $type2, bool $allow_interface_equality = true ): bool { if ($type1->hasMixed() || $type2->hasMixed()) { @@ -434,7 +436,7 @@ public static function canExpressionTypesBeIdentical( foreach ($type2->getAtomicTypes() as $type2_part) { //special cases for TIntRange because it can contain a part of the other type. //For exemple int<0,1> and positive-int can be identical but none contain the other - if (($type1_part instanceof Atomic\TIntRange && $type2_part instanceof Atomic\TPositiveInt)) { + if (($type1_part instanceof TIntRange && $type2_part instanceof TPositiveInt)) { $intersection_range = TIntRange::intersectIntRanges( TIntRange::convertToIntRange($type2_part), $type1_part @@ -442,7 +444,7 @@ public static function canExpressionTypesBeIdentical( return $intersection_range !== null; } - if ($type2_part instanceof Atomic\TIntRange && $type1_part instanceof Atomic\TPositiveInt) { + if ($type2_part instanceof TIntRange && $type1_part instanceof TPositiveInt) { $intersection_range = TIntRange::intersectIntRanges( TIntRange::convertToIntRange($type1_part), $type2_part @@ -450,7 +452,7 @@ public static function canExpressionTypesBeIdentical( return $intersection_range !== null; } - if ($type1_part instanceof Atomic\TIntRange && $type2_part instanceof Atomic\TIntRange) { + if ($type1_part instanceof TIntRange && $type2_part instanceof TIntRange) { $intersection_range = TIntRange::intersectIntRanges( $type1_part, $type2_part @@ -475,11 +477,11 @@ public static function canExpressionTypesBeIdentical( } /** - * @return list + * @return list */ private static function getTypeParts( Codebase $codebase, - Type\Union $union_type + Union $union_type ): array { $atomic_types = []; foreach ($union_type->getAtomicTypes() as $atomic_type) { @@ -496,7 +498,7 @@ private static function getTypeParts( true, true ); - if ($expanded instanceof Type\Atomic) { + if ($expanded instanceof Atomic) { array_push($atomic_types, $expanded); continue; } diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index b54e6568874..6ecc9177e44 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -14,11 +14,19 @@ use Psalm\IssueBuffer; use Psalm\Type; use Psalm\Type\Atomic\TArray; +use Psalm\Type\Atomic\TEmptyMixed; +use Psalm\Type\Atomic\TEnumCase; use Psalm\Type\Atomic\TFalse; +use Psalm\Type\Atomic\TFloat; +use Psalm\Type\Atomic\TInt; +use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TNonEmptyMixed; +use Psalm\Type\Atomic\TNonEmptyString; use Psalm\Type\Atomic\TString; use Psalm\Type\Atomic\TTrue; use Psalm\Type\Reconciler; +use Psalm\Type\Union; use function count; use function explode; @@ -30,7 +38,7 @@ class NegatedAssertionReconciler extends Reconciler { /** - * @param array> $template_type_map + * @param array> $template_type_map * @param string[] $suppressed_issues * @param 0|1|2 $failed_reconciliation */ @@ -39,7 +47,7 @@ public static function reconcile( string $assertion, bool $is_strict_equality, bool $is_loose_equality, - Type\Union $existing_var_type, + Union $existing_var_type, array $template_type_map, string $old_var_type_string, ?string $key, @@ -48,7 +56,7 @@ public static function reconcile( array $suppressed_issues, int &$failed_reconciliation, bool $inside_loop - ): Type\Union { + ): Union { $is_equality = $is_strict_equality || $is_loose_equality; // this is a specific value comparison type that cannot be negated @@ -87,7 +95,7 @@ public static function reconcile( ) { foreach ($existing_var_type->getAtomicTypes() as $atomic) { if (!$existing_var_type->hasMixed() - || $atomic instanceof Type\Atomic\TNonEmptyMixed + || $atomic instanceof TNonEmptyMixed ) { $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -228,9 +236,9 @@ public static function reconcile( $existing_var_type->removeType($assertion); if ($assertion === 'int') { - $existing_var_type->addType(new Type\Atomic\TFloat); + $existing_var_type->addType(new TFloat); } else { - $existing_var_type->addType(new Type\Atomic\TInt); + $existing_var_type->addType(new TInt); } $existing_var_type->from_calculation = false; @@ -256,7 +264,7 @@ public static function reconcile( if (strtolower($assertion) === 'traversable' && isset($existing_var_atomic_types['iterable']) ) { - /** @var Type\Atomic\TIterable */ + /** @var TIterable */ $iterable = $existing_var_atomic_types['iterable']; $existing_var_type->removeType('iterable'); $existing_var_type->addType(new TArray( @@ -371,7 +379,7 @@ public static function reconcile( $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; - return new Type\Union([new Type\Atomic\TEmptyMixed]); + return new Union([new TEmptyMixed]); } return $existing_var_type; @@ -385,14 +393,14 @@ private static function handleLiteralNegatedEquality( StatementsAnalyzer $statements_analyzer, string $assertion, int $bracket_pos, - Type\Union $existing_var_type, + Union $existing_var_type, string $old_var_type_string, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, bool $is_strict_equality - ): Type\Union { + ): Union { $scalar_type = substr($assertion, 0, $bracket_pos); $existing_var_atomic_types = $existing_var_type->getAtomicTypes(); @@ -435,7 +443,7 @@ private static function handleLiteralNegatedEquality( $did_remove_type = true; } } elseif ($assertion === 'string()') { - $existing_var_type->addType(new Type\Atomic\TNonEmptyString()); + $existing_var_type->addType(new TNonEmptyString()); } } elseif ($scalar_type === 'string') { $scalar_value = substr($assertion, $bracket_pos + 1, -1); @@ -460,7 +468,7 @@ private static function handleLiteralNegatedEquality( [$fq_enum_name, $case_name] = explode('::', substr($assertion, $bracket_pos + 1, -1)); foreach ($existing_var_type->getAtomicTypes() as $atomic_key => $atomic_type) { - if (get_class($atomic_type) === Type\Atomic\TNamedObject::class + if (get_class($atomic_type) === TNamedObject::class && $atomic_type->value === $fq_enum_name ) { $codebase = $statements_analyzer->getCodebase(); @@ -468,7 +476,7 @@ private static function handleLiteralNegatedEquality( $enum_storage = $codebase->classlike_storage_provider->get($fq_enum_name); if (!$enum_storage->is_enum || !$enum_storage->enum_cases) { - $scalar_var_type = new Type\Union([new Type\Atomic\TEnumCase($fq_enum_name, $case_name)]); + $scalar_var_type = new Union([new TEnumCase($fq_enum_name, $case_name)]); } else { $existing_var_type->removeType($atomic_type->getKey()); $did_remove_type = true; @@ -478,10 +486,10 @@ private static function handleLiteralNegatedEquality( continue; } - $existing_var_type->addType(new Type\Atomic\TEnumCase($fq_enum_name, $alt_case_name)); + $existing_var_type->addType(new TEnumCase($fq_enum_name, $alt_case_name)); } } - } elseif ($atomic_type instanceof Type\Atomic\TEnumCase + } elseif ($atomic_type instanceof TEnumCase && $atomic_type->value === $fq_enum_name && $atomic_type->case_name !== $case_name ) { diff --git a/src/Psalm/Internal/Type/ParseTree.php b/src/Psalm/Internal/Type/ParseTree.php index ac65fa30df2..fa3f0b3b6b9 100644 --- a/src/Psalm/Internal/Type/ParseTree.php +++ b/src/Psalm/Internal/Type/ParseTree.php @@ -1,4 +1,5 @@ type_tokens = $type_tokens; $this->type_token_count = count($type_tokens); - $this->parse_tree = new ParseTree\Root(); + $this->parse_tree = new Root(); $this->current_leaf = $this->parse_tree; } @@ -71,7 +91,7 @@ public function create(): ParseTree } $this->current_leaf = $this->current_leaf->parent; - } while (!$this->current_leaf instanceof ParseTree\GenericTree); + } while (!$this->current_leaf instanceof GenericTree); $this->current_leaf->terminated = true; @@ -84,7 +104,7 @@ public function create(): ParseTree } $this->current_leaf = $this->current_leaf->parent; - } while (!$this->current_leaf instanceof ParseTree\KeyedArrayTree); + } while (!$this->current_leaf instanceof KeyedArrayTree); $this->current_leaf->terminated = true; @@ -135,9 +155,9 @@ public function create(): ParseTree $this->parse_tree->cleanParents(); if ($this->current_leaf !== $this->parse_tree - && ($this->parse_tree instanceof ParseTree\GenericTree - || $this->parse_tree instanceof ParseTree\CallableTree - || $this->parse_tree instanceof ParseTree\KeyedArrayTree) + && ($this->parse_tree instanceof GenericTree + || $this->parse_tree instanceof CallableTree + || $this->parse_tree instanceof KeyedArrayTree) ) { throw new TypeParseTreeException( 'Unterminated bracket' @@ -172,7 +192,7 @@ private function createMethodParam(array $current_token, ParseTree $current_pare throw new TypeParseTreeException('Unexpected token after space'); } - $new_parent_leaf = new ParseTree\MethodParamTree( + $new_parent_leaf = new MethodParamTree( $current_token[0], $byref, $variadic, @@ -218,7 +238,7 @@ private function createMethodParam(array $current_token, ParseTree $current_pare private function handleOpenSquareBracket(): void { - if ($this->current_leaf instanceof ParseTree\Root) { + if ($this->current_leaf instanceof Root) { throw new TypeParseTreeException('Unexpected token ['); } @@ -244,13 +264,13 @@ private function handleOpenSquareBracket(): void throw new TypeParseTreeException('Unexpected token ['); } - $new_parent_leaf = new ParseTree\IndexedAccessTree($next_token[0], $current_parent); + $new_parent_leaf = new IndexedAccessTree($next_token[0], $current_parent); } else { - if ($this->current_leaf instanceof ParseTree\KeyedArrayPropertyTree) { + if ($this->current_leaf instanceof KeyedArrayPropertyTree) { throw new TypeParseTreeException('Unexpected token ['); } - $new_parent_leaf = new ParseTree\GenericTree('array', $current_parent); + $new_parent_leaf = new GenericTree('array', $current_parent); } $this->current_leaf->parent = $new_parent_leaf; @@ -269,17 +289,17 @@ private function handleOpenSquareBracket(): void private function handleOpenRoundBracket(): void { - if ($this->current_leaf instanceof ParseTree\Value) { + if ($this->current_leaf instanceof Value) { throw new TypeParseTreeException('Unrecognised token ('); } - $new_parent = !$this->current_leaf instanceof ParseTree\Root ? $this->current_leaf : null; + $new_parent = !$this->current_leaf instanceof Root ? $this->current_leaf : null; - $new_leaf = new ParseTree\EncapsulationTree( + $new_leaf = new EncapsulationTree( $new_parent ); - if ($this->current_leaf instanceof ParseTree\Root) { + if ($this->current_leaf instanceof Root) { $this->current_leaf = $this->parse_tree = $new_leaf; return; } @@ -297,7 +317,7 @@ private function handleClosedRoundBracket(): void if ($prev_token !== null && $prev_token[0] === '(' - && $this->current_leaf instanceof ParseTree\CallableTree + && $this->current_leaf instanceof CallableTree ) { return; } @@ -308,12 +328,12 @@ private function handleClosedRoundBracket(): void } $this->current_leaf = $this->current_leaf->parent; - } while (!$this->current_leaf instanceof ParseTree\EncapsulationTree - && !$this->current_leaf instanceof ParseTree\CallableTree - && !$this->current_leaf instanceof ParseTree\MethodTree); + } while (!$this->current_leaf instanceof EncapsulationTree + && !$this->current_leaf instanceof CallableTree + && !$this->current_leaf instanceof MethodTree); - if ($this->current_leaf instanceof ParseTree\EncapsulationTree - || $this->current_leaf instanceof ParseTree\CallableTree + if ($this->current_leaf instanceof EncapsulationTree + || $this->current_leaf instanceof CallableTree ) { $this->current_leaf->terminated = true; } @@ -321,7 +341,7 @@ private function handleClosedRoundBracket(): void private function handleComma(): void { - if ($this->current_leaf instanceof ParseTree\Root) { + if ($this->current_leaf instanceof Root) { throw new TypeParseTreeException('Unexpected token ,'); } @@ -331,19 +351,19 @@ private function handleComma(): void $context_node = $this->current_leaf; - if ($context_node instanceof ParseTree\GenericTree - || $context_node instanceof ParseTree\KeyedArrayTree - || $context_node instanceof ParseTree\CallableTree - || $context_node instanceof ParseTree\MethodTree + if ($context_node instanceof GenericTree + || $context_node instanceof KeyedArrayTree + || $context_node instanceof CallableTree + || $context_node instanceof MethodTree ) { $context_node = $context_node->parent; } while ($context_node - && !$context_node instanceof ParseTree\GenericTree - && !$context_node instanceof ParseTree\KeyedArrayTree - && !$context_node instanceof ParseTree\CallableTree - && !$context_node instanceof ParseTree\MethodTree + && !$context_node instanceof GenericTree + && !$context_node instanceof KeyedArrayTree + && !$context_node instanceof CallableTree + && !$context_node instanceof MethodTree ) { $context_node = $context_node->parent; } @@ -366,21 +386,21 @@ private function handleEllipsisOrEquals(array $type_token): void $current_parent = $this->current_leaf->parent; - if ($this->current_leaf instanceof ParseTree\MethodTree && $type_token[0] === '...') { + if ($this->current_leaf instanceof MethodTree && $type_token[0] === '...') { $this->createMethodParam($type_token, $this->current_leaf); return; } while ($current_parent - && !$current_parent instanceof ParseTree\CallableTree - && !$current_parent instanceof ParseTree\CallableParamTree + && !$current_parent instanceof CallableTree + && !$current_parent instanceof CallableParamTree ) { $this->current_leaf = $current_parent; $current_parent = $current_parent->parent; } if (!$current_parent) { - if ($this->current_leaf instanceof ParseTree\CallableTree + if ($this->current_leaf instanceof CallableTree && $type_token[0] === '...' ) { $current_parent = $this->current_leaf; @@ -389,11 +409,11 @@ private function handleEllipsisOrEquals(array $type_token): void } } - if ($current_parent instanceof ParseTree\CallableParamTree) { + if ($current_parent instanceof CallableParamTree) { throw new TypeParseTreeException('Cannot have variadic param with a default'); } - $new_leaf = new ParseTree\CallableParamTree($current_parent); + $new_leaf = new CallableParamTree($current_parent); $new_leaf->has_default = $type_token[0] === '='; $new_leaf->variadic = $type_token[0] === '...'; @@ -410,14 +430,14 @@ private function handleEllipsisOrEquals(array $type_token): void private function handleColon(): void { - if ($this->current_leaf instanceof ParseTree\Root) { + if ($this->current_leaf instanceof Root) { throw new TypeParseTreeException('Unexpected token :'); } $current_parent = $this->current_leaf->parent; - if ($this->current_leaf instanceof ParseTree\CallableTree) { - $new_parent_leaf = new ParseTree\CallableWithReturnTypeTree($current_parent); + if ($this->current_leaf instanceof CallableTree) { + $new_parent_leaf = new CallableWithReturnTypeTree($current_parent); $this->current_leaf->parent = $new_parent_leaf; $new_parent_leaf->children = [$this->current_leaf]; @@ -432,8 +452,8 @@ private function handleColon(): void return; } - if ($this->current_leaf instanceof ParseTree\MethodTree) { - $new_parent_leaf = new ParseTree\MethodWithReturnTypeTree($current_parent); + if ($this->current_leaf instanceof MethodTree) { + $new_parent_leaf = new MethodWithReturnTypeTree($current_parent); $this->current_leaf->parent = $new_parent_leaf; $new_parent_leaf->children = [$this->current_leaf]; @@ -448,19 +468,19 @@ private function handleColon(): void return; } - if ($current_parent instanceof ParseTree\KeyedArrayPropertyTree) { + if ($current_parent instanceof KeyedArrayPropertyTree) { return; } - while (($current_parent instanceof ParseTree\UnionTree - || $current_parent instanceof ParseTree\CallableWithReturnTypeTree) + while (($current_parent instanceof UnionTree + || $current_parent instanceof CallableWithReturnTypeTree) && $this->current_leaf->parent ) { $this->current_leaf = $this->current_leaf->parent; $current_parent = $this->current_leaf->parent; } - if ($current_parent instanceof ParseTree\ConditionalTree) { + if ($current_parent instanceof ConditionalTree) { if (count($current_parent->children) > 1) { throw new TypeParseTreeException('Cannot process colon in conditional twice'); } @@ -473,17 +493,17 @@ private function handleColon(): void throw new TypeParseTreeException('Cannot process colon without parent'); } - if (!$this->current_leaf instanceof ParseTree\Value) { + if (!$this->current_leaf instanceof Value) { throw new TypeParseTreeException('Unexpected LHS of property'); } - if (!$current_parent instanceof ParseTree\KeyedArrayTree) { + if (!$current_parent instanceof KeyedArrayTree) { throw new TypeParseTreeException('Saw : outside of object-like array'); } $prev_token = $this->t > 0 ? $this->type_tokens[$this->t - 1] : null; - $new_parent_leaf = new ParseTree\KeyedArrayPropertyTree($this->current_leaf->value, $current_parent); + $new_parent_leaf = new KeyedArrayPropertyTree($this->current_leaf->value, $current_parent); $new_parent_leaf->possibly_undefined = $prev_token !== null && $prev_token[0] === '?'; $this->current_leaf->parent = $new_parent_leaf; @@ -495,28 +515,28 @@ private function handleColon(): void private function handleSpace(): void { - if ($this->current_leaf instanceof ParseTree\Root) { + if ($this->current_leaf instanceof Root) { throw new TypeParseTreeException('Unexpected space'); } - if ($this->current_leaf instanceof ParseTree\KeyedArrayTree) { + if ($this->current_leaf instanceof KeyedArrayTree) { return; } $current_parent = $this->current_leaf->parent; - if ($current_parent instanceof ParseTree\CallableTree) { + if ($current_parent instanceof CallableTree) { return; } - while ($current_parent && !$current_parent instanceof ParseTree\MethodTree) { + while ($current_parent && !$current_parent instanceof MethodTree) { $this->current_leaf = $current_parent; $current_parent = $current_parent->parent; } $next_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null; - if (!$current_parent instanceof ParseTree\MethodTree || !$next_token) { + if (!$current_parent instanceof MethodTree || !$next_token) { throw new TypeParseTreeException('Unexpected space'); } @@ -530,26 +550,26 @@ private function handleQuestionMark(): void $next_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null; if ($next_token === null || $next_token[0] !== ':') { - while (($this->current_leaf instanceof ParseTree\Value - || $this->current_leaf instanceof ParseTree\UnionTree - || ($this->current_leaf instanceof ParseTree\KeyedArrayTree + while (($this->current_leaf instanceof Value + || $this->current_leaf instanceof UnionTree + || ($this->current_leaf instanceof KeyedArrayTree && $this->current_leaf->terminated) - || ($this->current_leaf instanceof ParseTree\GenericTree + || ($this->current_leaf instanceof GenericTree && $this->current_leaf->terminated) - || ($this->current_leaf instanceof ParseTree\EncapsulationTree + || ($this->current_leaf instanceof EncapsulationTree && $this->current_leaf->terminated) - || ($this->current_leaf instanceof ParseTree\CallableTree + || ($this->current_leaf instanceof CallableTree && $this->current_leaf->terminated) - || $this->current_leaf instanceof ParseTree\IntersectionTree) + || $this->current_leaf instanceof IntersectionTree) && $this->current_leaf->parent ) { $this->current_leaf = $this->current_leaf->parent; } - if ($this->current_leaf instanceof ParseTree\TemplateIsTree && $this->current_leaf->parent) { + if ($this->current_leaf instanceof TemplateIsTree && $this->current_leaf->parent) { $current_parent = $this->current_leaf->parent; - $new_leaf = new ParseTree\ConditionalTree( + $new_leaf = new ConditionalTree( $this->current_leaf, $this->current_leaf->parent ); @@ -560,17 +580,17 @@ private function handleQuestionMark(): void $current_parent->children[] = $new_leaf; $this->current_leaf = $new_leaf; } else { - $new_parent = !$this->current_leaf instanceof ParseTree\Root ? $this->current_leaf : null; + $new_parent = !$this->current_leaf instanceof Root ? $this->current_leaf : null; if (!$next_token) { throw new TypeParseTreeException('Unexpected token ?'); } - $new_leaf = new ParseTree\NullableTree( + $new_leaf = new NullableTree( $new_parent ); - if ($this->current_leaf instanceof ParseTree\Root) { + if ($this->current_leaf instanceof Root) { $this->current_leaf = $this->parse_tree = $new_leaf; return; } @@ -586,43 +606,43 @@ private function handleQuestionMark(): void private function handleBar(): void { - if ($this->current_leaf instanceof ParseTree\Root) { + if ($this->current_leaf instanceof Root) { throw new TypeParseTreeException('Unexpected token |'); } $current_parent = $this->current_leaf->parent; - if ($current_parent instanceof ParseTree\CallableWithReturnTypeTree) { + if ($current_parent instanceof CallableWithReturnTypeTree) { $this->current_leaf = $current_parent; $current_parent = $current_parent->parent; } - if ($current_parent instanceof ParseTree\NullableTree) { + if ($current_parent instanceof NullableTree) { $this->current_leaf = $current_parent; $current_parent = $current_parent->parent; } - if ($this->current_leaf instanceof ParseTree\UnionTree) { + if ($this->current_leaf instanceof UnionTree) { throw new TypeParseTreeException('Unexpected token |'); } - if ($current_parent instanceof ParseTree\UnionTree) { + if ($current_parent instanceof UnionTree) { $this->current_leaf = $current_parent; return; } - if ($current_parent instanceof ParseTree\IntersectionTree) { + if ($current_parent instanceof IntersectionTree) { $this->current_leaf = $current_parent; $current_parent = $this->current_leaf->parent; } - if ($current_parent instanceof ParseTree\TemplateIsTree) { - $new_parent_leaf = new ParseTree\UnionTree($this->current_leaf); + if ($current_parent instanceof TemplateIsTree) { + $new_parent_leaf = new UnionTree($this->current_leaf); $new_parent_leaf->children = [$this->current_leaf]; $new_parent_leaf->parent = $current_parent; $this->current_leaf->parent = $new_parent_leaf; } else { - $new_parent_leaf = new ParseTree\UnionTree($current_parent); + $new_parent_leaf = new UnionTree($current_parent); $new_parent_leaf->children = [$this->current_leaf]; $this->current_leaf->parent = $new_parent_leaf; } @@ -639,7 +659,7 @@ private function handleBar(): void private function handleAmpersand(): void { - if ($this->current_leaf instanceof ParseTree\Root) { + if ($this->current_leaf instanceof Root) { throw new TypeParseTreeException( 'Unexpected &' ); @@ -647,17 +667,17 @@ private function handleAmpersand(): void $current_parent = $this->current_leaf->parent; - if ($current_parent instanceof ParseTree\MethodTree) { + if ($current_parent instanceof MethodTree) { $this->createMethodParam($this->type_tokens[$this->t], $current_parent); return; } - if ($current_parent instanceof ParseTree\IntersectionTree) { + if ($current_parent instanceof IntersectionTree) { $this->current_leaf = $current_parent; return; } - $new_parent_leaf = new ParseTree\IntersectionTree($current_parent); + $new_parent_leaf = new IntersectionTree($current_parent); $new_parent_leaf->children = [$this->current_leaf]; $this->current_leaf->parent = $new_parent_leaf; @@ -686,14 +706,14 @@ private function handleIsOrAs(array $type_token): void if ($type_token[0] === 'as') { $next_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null; - if (!$this->current_leaf instanceof ParseTree\Value - || !$current_parent instanceof ParseTree\GenericTree + if (!$this->current_leaf instanceof Value + || !$current_parent instanceof GenericTree || !$next_token ) { throw new TypeParseTreeException('Unexpected token ' . $type_token[0]); } - $this->current_leaf = new ParseTree\TemplateAsTree( + $this->current_leaf = new TemplateAsTree( $this->current_leaf->value, $next_token[0], $current_parent @@ -701,8 +721,8 @@ private function handleIsOrAs(array $type_token): void $current_parent->children[] = $this->current_leaf; ++$this->t; - } elseif ($this->current_leaf instanceof ParseTree\Value) { - $this->current_leaf = new ParseTree\TemplateIsTree( + } elseif ($this->current_leaf instanceof Value) { + $this->current_leaf = new TemplateIsTree( $this->current_leaf->value, $current_parent ); @@ -717,9 +737,9 @@ private function handleIsOrAs(array $type_token): void /** @param array{0: string, 1: int, 2?: string} $type_token */ private function handleValue(array $type_token): void { - $new_parent = !$this->current_leaf instanceof ParseTree\Root ? $this->current_leaf : null; + $new_parent = !$this->current_leaf instanceof Root ? $this->current_leaf : null; - if ($this->current_leaf instanceof ParseTree\MethodTree && $type_token[0][0] === '$') { + if ($this->current_leaf instanceof MethodTree && $type_token[0][0] === '$') { $this->createMethodParam($type_token, $this->current_leaf); return; } @@ -728,7 +748,7 @@ private function handleValue(array $type_token): void switch ($next_token[0] ?? null) { case '<': - $new_leaf = new ParseTree\GenericTree( + $new_leaf = new GenericTree( $type_token[0], $new_parent ); @@ -736,7 +756,7 @@ private function handleValue(array $type_token): void break; case '{': - $new_leaf = new ParseTree\KeyedArrayTree( + $new_leaf = new KeyedArrayTree( $type_token[0], $new_parent ); @@ -749,15 +769,15 @@ private function handleValue(array $type_token): void ['callable', 'pure-callable', 'Closure', '\Closure', 'pure-Closure'], true )) { - $new_leaf = new ParseTree\CallableTree( + $new_leaf = new CallableTree( $type_token[0], $new_parent ); } elseif ($type_token[0] !== 'array' && $type_token[0][0] !== '\\' - && $this->current_leaf instanceof ParseTree\Root + && $this->current_leaf instanceof Root ) { - $new_leaf = new ParseTree\MethodTree( + $new_leaf = new MethodTree( $type_token[0], $new_parent ); @@ -773,9 +793,12 @@ private function handleValue(array $type_token): void case '::': $nexter_token = $this->t + 2 < $this->type_token_count ? $this->type_tokens[$this->t + 2] : null; - if ($this->current_leaf instanceof ParseTree\KeyedArrayTree) { + if ($this->current_leaf instanceof ParseTree\KeyedArrayTree + && $nexter_token + && strtolower($nexter_token[0]) !== 'class' + ) { throw new TypeParseTreeException( - 'Unexpected :: in array key' + ':: in array key is only allowed for ::class' ); } @@ -788,7 +811,7 @@ private function handleValue(array $type_token): void ); } - $new_leaf = new ParseTree\Value( + $new_leaf = new Value( $type_token[0] . '::' . $nexter_token[0], $type_token[1], $type_token[1] + 2 + strlen($nexter_token[0]), @@ -805,7 +828,7 @@ private function handleValue(array $type_token): void $type_token[0] = 'static'; } - $new_leaf = new ParseTree\Value( + $new_leaf = new Value( $type_token[0], $type_token[1], $type_token[1] + strlen($type_token[0]), @@ -815,7 +838,7 @@ private function handleValue(array $type_token): void break; } - if ($this->current_leaf instanceof ParseTree\Root) { + if ($this->current_leaf instanceof Root) { $this->current_leaf = $this->parse_tree = $new_leaf; return; } diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index e114165bc63..9050e4e25f6 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -1,4 +1,5 @@ hasMixed()) { return $existing_var_type; } @@ -539,13 +548,13 @@ private static function reconcileNonEmptyCountable( $did_remove_type = false; if ($array_atomic_type instanceof TArray) { - if (!$array_atomic_type instanceof Type\Atomic\TNonEmptyArray + if (!$array_atomic_type instanceof TNonEmptyArray || ($array_atomic_type->count < $min_count) ) { if ($array_atomic_type->getId() === 'array') { $existing_var_type->removeType('array'); } else { - $non_empty_array = new Type\Atomic\TNonEmptyArray( + $non_empty_array = new TNonEmptyArray( $array_atomic_type->type_params ); @@ -559,10 +568,10 @@ private static function reconcileNonEmptyCountable( $did_remove_type = true; } } elseif ($array_atomic_type instanceof TList) { - if (!$array_atomic_type instanceof Type\Atomic\TNonEmptyList + if (!$array_atomic_type instanceof TNonEmptyList || ($array_atomic_type->count < $min_count) ) { - $non_empty_list = new Type\Atomic\TNonEmptyList( + $non_empty_list = new TNonEmptyList( $array_atomic_type->type_param ); @@ -573,7 +582,7 @@ private static function reconcileNonEmptyCountable( $did_remove_type = true; $existing_var_type->addType($non_empty_list); } - } elseif ($array_atomic_type instanceof Type\Atomic\TKeyedArray) { + } elseif ($array_atomic_type instanceof TKeyedArray) { foreach ($array_atomic_type->properties as $property_type) { if ($property_type->possibly_undefined) { $did_remove_type = true; @@ -615,7 +624,7 @@ private static function reconcileExactlyCountable( $array_atomic_type = $existing_var_type->getAtomicTypes()['array']; if ($array_atomic_type instanceof TArray) { - $non_empty_array = new Type\Atomic\TNonEmptyArray( + $non_empty_array = new TNonEmptyArray( $array_atomic_type->type_params ); @@ -625,7 +634,7 @@ private static function reconcileExactlyCountable( $non_empty_array ); } elseif ($array_atomic_type instanceof TList) { - $non_empty_list = new Type\Atomic\TNonEmptyList( + $non_empty_list = new TNonEmptyList( $array_atomic_type->type_param ); @@ -660,28 +669,28 @@ private static function reconcilePositiveNumeric( $positive_types = []; foreach ($existing_var_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TLiteralInt) { + if ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value < 1) { $did_remove_type = true; } else { $positive_types[] = $atomic_type; } - } elseif ($atomic_type instanceof Type\Atomic\TPositiveInt) { + } elseif ($atomic_type instanceof TPositiveInt) { $positive_types[] = $atomic_type; - } elseif ($atomic_type instanceof Type\Atomic\TIntRange) { + } elseif ($atomic_type instanceof TIntRange) { if (!$atomic_type->isPositive()) { $did_remove_type = true; } - $positive_types[] = new Type\Atomic\TIntRange( + $positive_types[] = new TIntRange( $atomic_type->min_bound === null ? 1 : max(1, $atomic_type->min_bound), $atomic_type->max_bound === null ? null : max(1, $atomic_type->max_bound) ); } elseif (get_class($atomic_type) === TInt::class) { - $positive_types[] = new Type\Atomic\TPositiveInt(); + $positive_types[] = new TPositiveInt(); $did_remove_type = true; } else { // for now allow this check everywhere else - if (!$atomic_type instanceof Type\Atomic\TNull + if (!$atomic_type instanceof TNull && !$atomic_type instanceof TFalse ) { $positive_types[] = $atomic_type; @@ -710,7 +719,7 @@ private static function reconcilePositiveNumeric( } if ($positive_types) { - return new Type\Union($positive_types); + return new Union($positive_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -754,7 +763,7 @@ private static function reconcileHasMethod( && $codebase->methodExists($extra_type->value . '::' . $method_name) ) { $match_found = true; - } elseif ($extra_type instanceof Atomic\TObjectWithProperties) { + } elseif ($extra_type instanceof TObjectWithProperties) { $match_found = true; if (!isset($extra_type->methods[$method_name])) { @@ -766,7 +775,7 @@ private static function reconcileHasMethod( } if (!$match_found) { - $obj = new Atomic\TObjectWithProperties( + $obj = new TObjectWithProperties( [], [$method_name => $type->value . '::' . $method_name] ); @@ -774,7 +783,7 @@ private static function reconcileHasMethod( $did_remove_type = true; } } - } elseif ($type instanceof Atomic\TObjectWithProperties) { + } elseif ($type instanceof TObjectWithProperties) { $object_types[] = $type; if (!isset($type->methods[$method_name])) { @@ -782,7 +791,7 @@ private static function reconcileHasMethod( $did_remove_type = true; } } elseif ($type instanceof TObject || $type instanceof TMixed) { - $object_types[] = new Atomic\TObjectWithProperties( + $object_types[] = new TObjectWithProperties( [], [$method_name => 'object::' . $method_name] ); @@ -815,7 +824,7 @@ private static function reconcileHasMethod( } if ($object_types) { - return new Type\Union($object_types); + return new Union($object_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -859,7 +868,7 @@ private static function reconcileString( $type->from_docblock = false; } } elseif ($type instanceof TCallable) { - $string_types[] = new Type\Atomic\TCallableString; + $string_types[] = new TCallableString; $did_remove_type = true; } elseif ($type instanceof TNumeric) { $string_types[] = new TNumericString; @@ -907,7 +916,7 @@ private static function reconcileString( } if ($string_types) { - return new Type\Union($string_types); + return new Union($string_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1005,7 +1014,7 @@ private static function reconcileInt( } if ($int_types) { - return new Type\Union($int_types); + return new Union($int_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1084,7 +1093,7 @@ private static function reconcileBool( } if ($bool_types) { - return new Type\Union($bool_types); + return new Union($bool_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1159,7 +1168,7 @@ private static function reconcileScalar( } if ($scalar_types) { - return new Type\Union($scalar_types); + return new Union($scalar_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1251,7 +1260,7 @@ private static function reconcileNumeric( } if ($numeric_types) { - return new Type\Union($numeric_types); + return new Union($numeric_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1288,7 +1297,7 @@ private static function reconcileObject( if ($type->isObjectType()) { $object_types[] = $type; } elseif ($type instanceof TCallable) { - $object_types[] = new Type\Atomic\TCallableObject(); + $object_types[] = new TCallableObject(); $did_remove_type = true; } elseif ($type instanceof TTemplateParam && $type->as->isMixed() @@ -1315,12 +1324,12 @@ private static function reconcileObject( } $did_remove_type = true; - } elseif ($type instanceof Atomic\TIterable) { + } elseif ($type instanceof TIterable) { $clone_type = clone $type; self::refineArrayKey($clone_type->type_params[0]); - $object_types[] = new Type\Atomic\TGenericObject('Traversable', $clone_type->type_params); + $object_types[] = new TGenericObject('Traversable', $clone_type->type_params); $did_remove_type = true; } else { @@ -1344,7 +1353,7 @@ private static function reconcileObject( } if ($object_types) { - return new Type\Union($object_types); + return new Union($object_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1401,7 +1410,7 @@ private static function reconcileResource( } if ($resource_types) { - return new Type\Union($resource_types); + return new Union($resource_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1431,9 +1440,9 @@ private static function reconcileCountable( if ($existing_var_type->hasMixed() || $existing_var_type->hasTemplate()) { - return new Type\Union([ - new Type\Atomic\TArray([Type::getArrayKey(), Type::getMixed()]), - new Type\Atomic\TNamedObject('Countable'), + return new Union([ + new TArray([Type::getArrayKey(), Type::getMixed()]), + new TNamedObject('Countable'), ]); } @@ -1446,7 +1455,7 @@ private static function reconcileCountable( } elseif ($type instanceof TObject) { $iterable_types[] = new TNamedObject('Countable'); $did_remove_type = true; - } elseif ($type instanceof TNamedObject || $type instanceof Type\Atomic\TIterable) { + } elseif ($type instanceof TNamedObject || $type instanceof TIterable) { $countable = new TNamedObject('Countable'); $type->extra_types[$countable->getKey()] = $countable; $iterable_types[] = $type; @@ -1472,7 +1481,7 @@ private static function reconcileCountable( } if ($iterable_types) { - return new Type\Union($iterable_types); + return new Union($iterable_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1499,7 +1508,7 @@ private static function reconcileIterable( $existing_var_atomic_types = $existing_var_type->getAtomicTypes(); if ($existing_var_type->hasMixed() || $existing_var_type->hasTemplate()) { - return new Type\Union([new Type\Atomic\TIterable]); + return new Union([new TIterable]); } $iterable_types = []; @@ -1509,7 +1518,7 @@ private static function reconcileIterable( if ($type->isIterable($codebase)) { $iterable_types[] = $type; } elseif ($type instanceof TObject) { - $iterable_types[] = new Type\Atomic\TNamedObject('Traversable'); + $iterable_types[] = new TNamedObject('Traversable'); $did_remove_type = true; } else { $did_remove_type = true; @@ -1532,7 +1541,7 @@ private static function reconcileIterable( } if ($iterable_types) { - return new Type\Union($iterable_types); + return new Union($iterable_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1592,7 +1601,7 @@ private static function reconcileHasArrayKey( string $assertion ): Union { foreach ($existing_var_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_type instanceof TKeyedArray) { $is_class_string = false; if (strpos($assertion, '::class')) { @@ -1626,37 +1635,37 @@ private static function reconcileSuperiorTo( continue; } - if ($atomic_type instanceof Atomic\TIntRange) { + if ($atomic_type instanceof TIntRange) { $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->min_bound === null) { $atomic_type->min_bound = $assertion_value; } else { - $atomic_type->min_bound = Atomic\TIntRange::getNewHighestBound( + $atomic_type->min_bound = TIntRange::getNewHighestBound( $assertion_value, $atomic_type->min_bound ); } $existing_var_type->addType($atomic_type); - } elseif ($atomic_type instanceof Atomic\TLiteralInt) { + } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value < $assertion_value) { $existing_var_type->removeType($atomic_type->getKey()); } /*elseif ($inside_loop) { //when inside a loop, allow the range to extends the type $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->value < $assertion_value) { - $existing_var_type->addType(new Atomic\TIntRange($atomic_type->value, $assertion_value)); + $existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value)); } else { - $existing_var_type->addType(new Atomic\TIntRange($assertion_value, $atomic_type->value)); + $existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value)); } }*/ - } elseif ($atomic_type instanceof Atomic\TPositiveInt) { + } elseif ($atomic_type instanceof TPositiveInt) { if ($assertion_value > 1) { $existing_var_type->removeType($atomic_type->getKey()); - $existing_var_type->addType(new Atomic\TIntRange($assertion_value, null)); + $existing_var_type->addType(new TIntRange($assertion_value, null)); } } elseif ($atomic_type instanceof TInt) { $existing_var_type->removeType($atomic_type->getKey()); - $existing_var_type->addType(new Atomic\TIntRange($assertion_value, null)); + $existing_var_type->addType(new TIntRange($assertion_value, null)); } } @@ -1674,7 +1683,7 @@ private static function reconcileInferiorTo( continue; } - if ($atomic_type instanceof Atomic\TIntRange) { + if ($atomic_type instanceof TIntRange) { $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->max_bound === null) { $atomic_type->max_bound = $assertion_value; @@ -1682,26 +1691,26 @@ private static function reconcileInferiorTo( $atomic_type->max_bound = min($atomic_type->max_bound, $assertion_value); } $existing_var_type->addType($atomic_type); - } elseif ($atomic_type instanceof Atomic\TLiteralInt) { + } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value > $assertion_value) { $existing_var_type->removeType($atomic_type->getKey()); } /* elseif ($inside_loop) { //when inside a loop, allow the range to extends the type $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->value < $assertion_value) { - $existing_var_type->addType(new Atomic\TIntRange($atomic_type->value, $assertion_value)); + $existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value)); } else { - $existing_var_type->addType(new Atomic\TIntRange($assertion_value, $atomic_type->value)); + $existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value)); } }*/ - } elseif ($atomic_type instanceof Atomic\TPositiveInt) { + } elseif ($atomic_type instanceof TPositiveInt) { $existing_var_type->removeType($atomic_type->getKey()); if ($assertion_value >= 1) { - $existing_var_type->addType(new Atomic\TIntRange(1, $assertion_value)); + $existing_var_type->addType(new TIntRange(1, $assertion_value)); } } elseif ($atomic_type instanceof TInt) { $existing_var_type->removeType($atomic_type->getKey()); - $existing_var_type->addType(new Atomic\TIntRange(null, $assertion_value)); + $existing_var_type->addType(new TIntRange(null, $assertion_value)); } } @@ -1727,7 +1736,7 @@ private static function reconcileTraversable( $existing_var_atomic_types = $existing_var_type->getAtomicTypes(); if ($existing_var_type->hasMixed() || $existing_var_type->hasTemplate()) { - return new Type\Union([new Type\Atomic\TNamedObject('Traversable')]); + return new Union([new TNamedObject('Traversable')]); } $traversable_types = []; @@ -1736,9 +1745,9 @@ private static function reconcileTraversable( foreach ($existing_var_atomic_types as $type) { if ($type->hasTraversableInterface($codebase)) { $traversable_types[] = $type; - } elseif ($type instanceof Atomic\TIterable) { + } elseif ($type instanceof TIterable) { $clone_type = clone $type; - $traversable_types[] = new Atomic\TGenericObject('Traversable', $clone_type->type_params); + $traversable_types[] = new TGenericObject('Traversable', $clone_type->type_params); $did_remove_type = true; } elseif ($type instanceof TObject) { $traversable_types[] = new TNamedObject('Traversable'); @@ -1769,7 +1778,7 @@ private static function reconcileTraversable( } if ($traversable_types) { - return new Type\Union($traversable_types); + return new Union($traversable_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -1813,7 +1822,7 @@ private static function reconcileArray( ]); $did_remove_type = true; - } elseif ($type instanceof Atomic\TIterable) { + } elseif ($type instanceof TIterable) { $clone_type = clone $type; self::refineArrayKey($clone_type->type_params[0]); @@ -1939,7 +1948,7 @@ private static function reconcileList( ]); $did_remove_type = true; - } elseif ($type instanceof Atomic\TIterable) { + } elseif ($type instanceof TIterable) { $clone_type = clone $type; $array_types[] = new TList($clone_type->type_params[1]); @@ -1999,7 +2008,7 @@ private static function reconcileStringArrayAccess( if ($existing_var_type->hasMixed() || $existing_var_type->hasTemplate()) { return new Union([ - new Atomic\TNonEmptyArray([Type::getArrayKey(), Type::getMixed()]), + new TNonEmptyArray([Type::getArrayKey(), Type::getMixed()]), new TNamedObject('ArrayAccess'), ]); } @@ -2009,9 +2018,9 @@ private static function reconcileStringArrayAccess( foreach ($existing_var_atomic_types as $type) { if ($type->isArrayAccessibleWithStringKey($codebase)) { if (get_class($type) === TArray::class) { - $array_types[] = new Atomic\TNonEmptyArray($type->type_params); + $array_types[] = new TNonEmptyArray($type->type_params); } elseif (get_class($type) === TList::class) { - $array_types[] = new Atomic\TNonEmptyList($type->type_param); + $array_types[] = new TNonEmptyList($type->type_param); } else { $array_types[] = $type; } @@ -2036,7 +2045,7 @@ private static function reconcileStringArrayAccess( } if ($array_types) { - return new Type\Union($array_types); + return new Union($array_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -2071,7 +2080,7 @@ private static function reconcileIntArrayAccess( foreach ($existing_var_atomic_types as $type) { if ($type->isArrayAccessibleWithIntOrStringKey($codebase)) { if (get_class($type) === TArray::class) { - $array_types[] = new Atomic\TNonEmptyArray($type->type_params); + $array_types[] = new TNonEmptyArray($type->type_params); } else { $array_types[] = $type; } @@ -2133,7 +2142,7 @@ private static function reconcileCallable( if ($type->isCallableType()) { $callable_types[] = $type; } elseif ($type instanceof TObject) { - $callable_types[] = new Type\Atomic\TCallableObject(); + $callable_types[] = new TCallableObject(); $did_remove_type = true; } elseif ($type instanceof TNamedObject && $codebase->classExists($type->value) @@ -2141,12 +2150,12 @@ private static function reconcileCallable( ) { $callable_types[] = $type; } elseif (get_class($type) === TString::class - || get_class($type) === Type\Atomic\TNonEmptyString::class - || get_class($type) === Type\Atomic\TNonFalsyString::class + || get_class($type) === TNonEmptyString::class + || get_class($type) === TNonFalsyString::class ) { - $callable_types[] = new Type\Atomic\TCallableString(); + $callable_types[] = new TCallableString(); $did_remove_type = true; - } elseif (get_class($type) === Type\Atomic\TLiteralString::class + } elseif (get_class($type) === TLiteralString::class && InternalCallMapHandler::inCallMap($type->value) ) { $callable_types[] = $type; @@ -2296,8 +2305,8 @@ private static function reconcileFalsyOrEmpty( $existing_var_type->removeType('array'); $existing_var_type->addType(new TArray( [ - new Type\Union([new TEmpty()]), - new Type\Union([new TEmpty()]), + new Union([new TEmpty()]), + new Union([new TEmpty()]), ] )); } diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index 57fdf9d811a..0aec7c76fbc 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -5,17 +5,24 @@ use Psalm\CodeLocation; use Psalm\Internal\Codebase\InternalCallMapHandler; use Psalm\Type; -use Psalm\Type\Atomic; use Psalm\Type\Atomic\Scalar; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; use Psalm\Type\Atomic\TBool; use Psalm\Type\Atomic\TCallable; +use Psalm\Type\Atomic\TCallableArray; +use Psalm\Type\Atomic\TCallableObject; +use Psalm\Type\Atomic\TCallableString; use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TFloat; +use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TInt; +use Psalm\Type\Atomic\TIntRange; +use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; +use Psalm\Type\Atomic\TLiteralInt; +use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TLowercaseString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; @@ -29,6 +36,7 @@ use Psalm\Type\Atomic\TNonFalsyString; use Psalm\Type\Atomic\TNonspecificLiteralString; use Psalm\Type\Atomic\TNumeric; +use Psalm\Type\Atomic\TPositiveInt; use Psalm\Type\Atomic\TScalar; use Psalm\Type\Atomic\TString; use Psalm\Type\Atomic\TTemplateParam; @@ -50,7 +58,7 @@ class SimpleNegatedAssertionReconciler extends Reconciler */ public static function reconcile( string $assertion, - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key = null, bool $negated = false, ?CodeLocation $code_location = null, @@ -59,7 +67,7 @@ public static function reconcile( bool $is_equality = false, bool $is_strict_equality = false, bool $inside_loop = false - ): ?Type\Union { + ): ?Union { if ($assertion === 'object' && !$existing_var_type->hasMixed()) { return self::reconcileObject( $existing_var_type, @@ -263,10 +271,10 @@ public static function reconcile( } private static function reconcileCallable( - Type\Union $existing_var_type - ): Type\Union { + Union $existing_var_type + ): Union { foreach ($existing_var_type->getAtomicTypes() as $atomic_key => $type) { - if ($type instanceof Type\Atomic\TLiteralString + if ($type instanceof TLiteralString && InternalCallMapHandler::inCallMap($type->value) ) { $existing_var_type->removeType($atomic_key); @@ -285,14 +293,14 @@ private static function reconcileCallable( * @param 0|1|2 $failed_reconciliation */ private static function reconcileBool( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $non_bool_types = []; $did_remove_type = false; @@ -340,7 +348,7 @@ private static function reconcileBool( } if ($non_bool_types) { - return new Type\Union($non_bool_types); + return new Union($non_bool_types); } $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; @@ -353,7 +361,7 @@ private static function reconcileBool( * @param 0|1|2 $failed_reconciliation */ private static function reconcileNonEmptyCountable( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, @@ -361,7 +369,7 @@ private static function reconcileNonEmptyCountable( int &$failed_reconciliation, bool $is_equality, ?int $min_count - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $existing_var_atomic_types = $existing_var_type->getAtomicTypes(); @@ -369,8 +377,8 @@ private static function reconcileNonEmptyCountable( $array_atomic_type = $existing_var_atomic_types['array']; $did_remove_type = false; - if (($array_atomic_type instanceof Type\Atomic\TNonEmptyArray - || $array_atomic_type instanceof Type\Atomic\TNonEmptyList) + if (($array_atomic_type instanceof TNonEmptyArray + || $array_atomic_type instanceof TNonEmptyList) && ($min_count === null || $array_atomic_type->count >= $min_count) ) { @@ -383,12 +391,12 @@ private static function reconcileNonEmptyCountable( if (!$min_count) { $existing_var_type->addType(new TArray( [ - new Type\Union([new TEmpty]), - new Type\Union([new TEmpty]), + new Union([new TEmpty]), + new Union([new TEmpty]), ] )); } - } elseif ($array_atomic_type instanceof Type\Atomic\TKeyedArray) { + } elseif ($array_atomic_type instanceof TKeyedArray) { $did_remove_type = true; foreach ($array_atomic_type->properties as $property_type) { @@ -426,14 +434,14 @@ private static function reconcileNonEmptyCountable( * @param 0|1|2 $failed_reconciliation */ private static function reconcileNull( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $did_remove_type = false; @@ -493,14 +501,14 @@ private static function reconcileNull( * @param 0|1|2 $failed_reconciliation */ private static function reconcileFalse( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $did_remove_type = $existing_var_type->hasScalar(); @@ -561,7 +569,7 @@ private static function reconcileFalse( */ private static function reconcileFalsyOrEmpty( string $assertion, - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, @@ -570,7 +578,7 @@ private static function reconcileFalsyOrEmpty( bool $is_equality, bool $is_strict_equality, bool $recursive_check - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); //empty is used a lot to check for array offset existence, so we have to silent errors a lot @@ -708,10 +716,10 @@ private static function reconcileFalsyOrEmpty( if ($literal_type->contains(0)) { $existing_var_type->removeType($int_key); if ($literal_type->min_bound === null || $literal_type->min_bound <= -1) { - $existing_var_type->addType(new Type\Atomic\TIntRange($literal_type->min_bound, -1)); + $existing_var_type->addType(new TIntRange($literal_type->min_bound, -1)); } if ($literal_type->max_bound === null || $literal_type->max_bound >= 1) { - $existing_var_type->addType(new Type\Atomic\TIntRange(1, $literal_type->max_bound)); + $existing_var_type->addType(new TIntRange(1, $literal_type->max_bound)); } } } @@ -760,14 +768,14 @@ private static function reconcileFalsyOrEmpty( * @param 0|1|2 $failed_reconciliation */ private static function reconcileScalar( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $non_scalar_types = []; $did_remove_type = false; @@ -829,7 +837,7 @@ private static function reconcileScalar( } if ($non_scalar_types) { - $type = new Type\Union($non_scalar_types); + $type = new Union($non_scalar_types); $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues; $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues; $type->from_docblock = $existing_var_type->from_docblock; @@ -846,14 +854,14 @@ private static function reconcileScalar( * @param 0|1|2 $failed_reconciliation */ private static function reconcileObject( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $non_object_types = []; $did_remove_type = false; @@ -885,13 +893,13 @@ private static function reconcileObject( $non_object_types[] = $type; } } elseif ($type instanceof TCallable) { - $non_object_types[] = new Atomic\TCallableArray([ + $non_object_types[] = new TCallableArray([ Type::getArrayKey(), Type::getMixed() ]); - $non_object_types[] = new Atomic\TCallableString(); + $non_object_types[] = new TCallableString(); $did_remove_type = true; - } elseif ($type instanceof Atomic\TIterable) { + } elseif ($type instanceof TIterable) { $clone_type = clone $type; self::refineArrayKey($clone_type->type_params[0]); @@ -930,7 +938,7 @@ private static function reconcileObject( } if ($non_object_types) { - $type = new Type\Union($non_object_types); + $type = new Union($non_object_types); $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues; $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues; $type->from_docblock = $existing_var_type->from_docblock; @@ -947,14 +955,14 @@ private static function reconcileObject( * @param 0|1|2 $failed_reconciliation */ private static function reconcileNumeric( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $non_numeric_types = []; $did_remove_type = $existing_var_type->hasString() @@ -1020,7 +1028,7 @@ private static function reconcileNumeric( } if ($non_numeric_types) { - $type = new Type\Union($non_numeric_types); + $type = new Union($non_numeric_types); $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues; $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues; $type->from_docblock = $existing_var_type->from_docblock; @@ -1037,14 +1045,14 @@ private static function reconcileNumeric( * @param 0|1|2 $failed_reconciliation */ private static function reconcileInt( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $non_int_types = []; $did_remove_type = false; @@ -1120,7 +1128,7 @@ private static function reconcileInt( } if ($non_int_types) { - $type = new Type\Union($non_int_types); + $type = new Union($non_int_types); $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues; $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues; $type->from_docblock = $existing_var_type->from_docblock; @@ -1137,14 +1145,14 @@ private static function reconcileInt( * @param 0|1|2 $failed_reconciliation */ private static function reconcileFloat( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $non_float_types = []; $did_remove_type = false; @@ -1215,7 +1223,7 @@ private static function reconcileFloat( } if ($non_float_types) { - $type = new Type\Union($non_float_types); + $type = new Union($non_float_types); $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues; $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues; $type->from_docblock = $existing_var_type->from_docblock; @@ -1232,14 +1240,14 @@ private static function reconcileFloat( * @param 0|1|2 $failed_reconciliation */ private static function reconcileString( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $non_string_types = []; $did_remove_type = $existing_var_type->hasScalar(); @@ -1274,11 +1282,11 @@ private static function reconcileString( $non_string_types[] = new TInt(); $did_remove_type = true; } elseif ($type instanceof TCallable) { - $non_string_types[] = new Atomic\TCallableArray([ + $non_string_types[] = new TCallableArray([ Type::getArrayKey(), Type::getMixed() ]); - $non_string_types[] = new Atomic\TCallableObject(); + $non_string_types[] = new TCallableObject(); $did_remove_type = true; } elseif ($type instanceof TNumeric) { $non_string_types[] = $type; @@ -1319,7 +1327,7 @@ private static function reconcileString( } if ($non_string_types) { - $type = new Type\Union($non_string_types); + $type = new Union($non_string_types); $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues; $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues; $type->from_docblock = $existing_var_type->from_docblock; @@ -1336,14 +1344,14 @@ private static function reconcileString( * @param 0|1|2 $failed_reconciliation */ private static function reconcileArray( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $non_array_types = []; $did_remove_type = $existing_var_type->hasScalar(); @@ -1375,12 +1383,12 @@ private static function reconcileArray( $non_array_types[] = $type; } } elseif ($type instanceof TCallable) { - $non_array_types[] = new Atomic\TCallableString(); - $non_array_types[] = new Atomic\TCallableObject(); + $non_array_types[] = new TCallableString(); + $non_array_types[] = new TCallableObject(); $did_remove_type = true; - } elseif ($type instanceof Atomic\TIterable) { + } elseif ($type instanceof TIterable) { if (!$type->type_params[0]->isMixed() || !$type->type_params[1]->isMixed()) { - $non_array_types[] = new Atomic\TGenericObject('Traversable', $type->type_params); + $non_array_types[] = new TGenericObject('Traversable', $type->type_params); } else { $non_array_types[] = new TNamedObject('Traversable'); } @@ -1388,7 +1396,7 @@ private static function reconcileArray( $did_remove_type = true; } elseif (!$type instanceof TArray && !$type instanceof TKeyedArray - && !$type instanceof Atomic\TList + && !$type instanceof TList ) { $non_array_types[] = $type; } else { @@ -1420,7 +1428,7 @@ private static function reconcileArray( } if ($non_array_types) { - $type = new Type\Union($non_array_types); + $type = new Union($non_array_types); $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues; $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues; $type->from_docblock = $existing_var_type->from_docblock; @@ -1437,14 +1445,14 @@ private static function reconcileArray( * @param 0|1|2 $failed_reconciliation */ private static function reconcileResource( - Type\Union $existing_var_type, + Union $existing_var_type, ?string $key, bool $negated, ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, bool $is_equality - ): Type\Union { + ): Union { $old_var_type_string = $existing_var_type->getId(); $did_remove_type = false; @@ -1507,37 +1515,37 @@ private static function reconcileSuperiorTo(Union $existing_var_type, string $as continue; } - if ($atomic_type instanceof Atomic\TIntRange) { + if ($atomic_type instanceof TIntRange) { $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->max_bound === null) { $atomic_type->max_bound = $assertion_value; } else { - $atomic_type->max_bound = Atomic\TIntRange::getNewLowestBound( + $atomic_type->max_bound = TIntRange::getNewLowestBound( $assertion_value, $atomic_type->max_bound ); } $existing_var_type->addType($atomic_type); - } elseif ($atomic_type instanceof Atomic\TLiteralInt) { + } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value > $assertion_value) { $existing_var_type->removeType($atomic_type->getKey()); } /*elseif ($inside_loop) { //when inside a loop, allow the range to extends the type $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->value < $assertion_value) { - $existing_var_type->addType(new Atomic\TIntRange($atomic_type->value, $assertion_value)); + $existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value)); } else { - $existing_var_type->addType(new Atomic\TIntRange($assertion_value, $atomic_type->value)); + $existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value)); } }*/ - } elseif ($atomic_type instanceof Atomic\TPositiveInt) { + } elseif ($atomic_type instanceof TPositiveInt) { $existing_var_type->removeType($atomic_type->getKey()); if ($assertion_value >= 1) { - $existing_var_type->addType(new Atomic\TIntRange(1, $assertion_value)); + $existing_var_type->addType(new TIntRange(1, $assertion_value)); } } elseif ($atomic_type instanceof TInt) { $existing_var_type->removeType($atomic_type->getKey()); - $existing_var_type->addType(new Atomic\TIntRange(null, $assertion_value)); + $existing_var_type->addType(new TIntRange(null, $assertion_value)); } } @@ -1552,7 +1560,7 @@ private static function reconcileInferiorTo(Union $existing_var_type, string $as continue; } - if ($atomic_type instanceof Atomic\TIntRange) { + if ($atomic_type instanceof TIntRange) { $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->min_bound === null) { $atomic_type->min_bound = $assertion_value; @@ -1560,26 +1568,26 @@ private static function reconcileInferiorTo(Union $existing_var_type, string $as $atomic_type->min_bound = max($atomic_type->min_bound, $assertion_value); } $existing_var_type->addType($atomic_type); - } elseif ($atomic_type instanceof Atomic\TLiteralInt) { + } elseif ($atomic_type instanceof TLiteralInt) { if ($atomic_type->value < $assertion_value) { $existing_var_type->removeType($atomic_type->getKey()); } /* elseif ($inside_loop) { //when inside a loop, allow the range to extends the type $existing_var_type->removeType($atomic_type->getKey()); if ($atomic_type->value < $assertion_value) { - $existing_var_type->addType(new Atomic\TIntRange($atomic_type->value, $assertion_value)); + $existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value)); } else { - $existing_var_type->addType(new Atomic\TIntRange($assertion_value, $atomic_type->value)); + $existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value)); } }*/ - } elseif ($atomic_type instanceof Atomic\TPositiveInt) { + } elseif ($atomic_type instanceof TPositiveInt) { if ($assertion_value > 1) { $existing_var_type->removeType($atomic_type->getKey()); - $existing_var_type->addType(new Atomic\TIntRange($assertion_value, null)); + $existing_var_type->addType(new TIntRange($assertion_value, null)); } } elseif ($atomic_type instanceof TInt) { $existing_var_type->removeType($atomic_type->getKey()); - $existing_var_type->addType(new Atomic\TIntRange($assertion_value, null)); + $existing_var_type->addType(new TIntRange($assertion_value, null)); } } diff --git a/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php b/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php index 6acec152d26..d0aff71b2f6 100644 --- a/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php +++ b/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php @@ -8,10 +8,20 @@ use Psalm\Internal\Type\TemplateBound; use Psalm\Internal\Type\TemplateStandinTypeReplacer; use Psalm\Type; -use Psalm\Type\Atomic; +use Psalm\Type\Atomic\TClassString; +use Psalm\Type\Atomic\TConditional; +use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIterable; +use Psalm\Type\Atomic\TKeyedArray; +use Psalm\Type\Atomic\TLiteralInt; +use Psalm\Type\Atomic\TLiteralString; +use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TObject; +use Psalm\Type\Atomic\TObjectWithProperties; +use Psalm\Type\Atomic\TTemplateIndexedAccess; use Psalm\Type\Atomic\TTemplateParam; +use Psalm\Type\Atomic\TTemplateParamClass; use Psalm\Type\Union; use UnexpectedValueException; @@ -41,7 +51,7 @@ public static function replace( foreach ($union->getAtomicTypes() as $key => $atomic_type) { $atomic_type->replaceTemplateTypesWithArgTypes($template_result, $codebase); - if ($atomic_type instanceof Atomic\TTemplateParam) { + if ($atomic_type instanceof TTemplateParam) { $template_type = null; $traversed_type = TemplateStandinTypeReplacer::getRootTemplateType( @@ -66,13 +76,13 @@ public static function replace( if ($atomic_template_type instanceof TNamedObject || $atomic_template_type instanceof TTemplateParam || $atomic_template_type instanceof TIterable - || $atomic_template_type instanceof Atomic\TObjectWithProperties + || $atomic_template_type instanceof TObjectWithProperties ) { $atomic_template_type->extra_types = array_merge( $atomic_type->extra_types, $atomic_template_type->extra_types ?: [] ); - } elseif ($atomic_template_type instanceof Atomic\TObject) { + } elseif ($atomic_template_type instanceof TObject) { $first_atomic_type = array_shift($atomic_type->extra_types); if ($atomic_type->extra_types) { @@ -123,14 +133,14 @@ public static function replace( $keys_to_unset[] = $key; foreach ($template_type->getAtomicTypes() as $template_type_part) { - if ($template_type_part instanceof Atomic\TMixed) { + if ($template_type_part instanceof TMixed) { $is_mixed = true; } $new_types[] = $template_type_part; } } - } elseif ($atomic_type instanceof Atomic\TTemplateParamClass) { + } elseif ($atomic_type instanceof TTemplateParamClass) { $template_type = isset($inferred_lower_bounds[$atomic_type->param_name][$atomic_type->defining_class]) ? clone TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds( $inferred_lower_bounds[$atomic_type->param_name][$atomic_type->defining_class], @@ -142,19 +152,19 @@ public static function replace( if ($template_type) { foreach ($template_type->getAtomicTypes() as $template_type_part) { - if ($template_type_part instanceof Atomic\TMixed - || $template_type_part instanceof Atomic\TObject + if ($template_type_part instanceof TMixed + || $template_type_part instanceof TObject ) { - $class_template_type = new Atomic\TClassString(); - } elseif ($template_type_part instanceof Atomic\TNamedObject) { - $class_template_type = new Atomic\TClassString( + $class_template_type = new TClassString(); + } elseif ($template_type_part instanceof TNamedObject) { + $class_template_type = new TClassString( $template_type_part->value, $template_type_part ); - } elseif ($template_type_part instanceof Atomic\TTemplateParam) { + } elseif ($template_type_part instanceof TTemplateParam) { $first_atomic_type = $template_type_part->as->getSingleAtomic(); - $class_template_type = new Atomic\TTemplateParamClass( + $class_template_type = new TTemplateParamClass( $template_type_part->param_name, $template_type_part->as->getId(), $first_atomic_type instanceof TNamedObject ? $first_atomic_type : null, @@ -168,7 +178,7 @@ public static function replace( $keys_to_unset[] = $key; $new_types[] = $class_template_type; } - } elseif ($atomic_type instanceof Atomic\TTemplateIndexedAccess) { + } elseif ($atomic_type instanceof TTemplateIndexedAccess) { $keys_to_unset[] = $key; $template_type = null; @@ -196,9 +206,9 @@ public static function replace( $array_template_type = $array_template_type->getSingleAtomic(); $offset_template_type = $offset_template_type->getSingleAtomic(); - if ($array_template_type instanceof Atomic\TKeyedArray - && ($offset_template_type instanceof Atomic\TLiteralString - || $offset_template_type instanceof Atomic\TLiteralInt) + if ($array_template_type instanceof TKeyedArray + && ($offset_template_type instanceof TLiteralString + || $offset_template_type instanceof TLiteralInt) && isset($array_template_type->properties[$offset_template_type->value]) ) { $template_type = clone $array_template_type->properties[$offset_template_type->value]; @@ -208,16 +218,16 @@ public static function replace( if ($template_type) { foreach ($template_type->getAtomicTypes() as $template_type_part) { - if ($template_type_part instanceof Atomic\TMixed) { + if ($template_type_part instanceof TMixed) { $is_mixed = true; } $new_types[] = $template_type_part; } } else { - $new_types[] = new Atomic\TMixed(); + $new_types[] = new TMixed(); } - } elseif ($atomic_type instanceof Atomic\TConditional + } elseif ($atomic_type instanceof TConditional && $codebase ) { $template_type = isset($inferred_lower_bounds[$atomic_type->param_name][$atomic_type->defining_class]) @@ -249,7 +259,7 @@ public static function replace( foreach ($template_type->getAtomicTypes() as $candidate_atomic_type) { if (UnionTypeComparator::isContainedBy( $codebase, - new Type\Union([$candidate_atomic_type]), + new Union([$candidate_atomic_type]), $atomic_type->conditional_type, false, false, @@ -257,14 +267,14 @@ public static function replace( false, false ) - && (!$candidate_atomic_type instanceof Type\Atomic\TInt + && (!$candidate_atomic_type instanceof TInt || $atomic_type->conditional_type->getId() !== 'float') ) { $matching_if_types[] = $candidate_atomic_type; } elseif (!UnionTypeComparator::isContainedBy( $codebase, $atomic_type->conditional_type, - new Type\Union([$candidate_atomic_type]), + new Union([$candidate_atomic_type]), false, false, null, @@ -275,8 +285,8 @@ public static function replace( } } - $if_candidate_type = $matching_if_types ? new Type\Union($matching_if_types) : null; - $else_candidate_type = $matching_else_types ? new Type\Union($matching_else_types) : null; + $if_candidate_type = $matching_if_types ? new Union($matching_if_types) : null; + $else_candidate_type = $matching_else_types ? new Union($matching_else_types) : null; if ($if_candidate_type && UnionTypeComparator::isContainedBy( diff --git a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php index e9e66529119..e29f9d68fae 100644 --- a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php +++ b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php @@ -11,6 +11,25 @@ use Psalm\Internal\Type\TypeCombiner; use Psalm\Type; use Psalm\Type\Atomic; +use Psalm\Type\Atomic\TArray; +use Psalm\Type\Atomic\TCallable; +use Psalm\Type\Atomic\TClassString; +use Psalm\Type\Atomic\TClosure; +use Psalm\Type\Atomic\TDependentGetClass; +use Psalm\Type\Atomic\TGenericObject; +use Psalm\Type\Atomic\TIterable; +use Psalm\Type\Atomic\TKeyedArray; +use Psalm\Type\Atomic\TList; +use Psalm\Type\Atomic\TLiteralClassString; +use Psalm\Type\Atomic\TLiteralInt; +use Psalm\Type\Atomic\TLiteralString; +use Psalm\Type\Atomic\TNamedObject; +use Psalm\Type\Atomic\TObject; +use Psalm\Type\Atomic\TObjectWithProperties; +use Psalm\Type\Atomic\TTemplateIndexedAccess; +use Psalm\Type\Atomic\TTemplateKeyOf; +use Psalm\Type\Atomic\TTemplateParam; +use Psalm\Type\Atomic\TTemplateParamClass; use Psalm\Type\Union; use Throwable; @@ -153,7 +172,7 @@ private static function handleAtomicStandin( $key = substr($key, 0, $bracket_pos); } - if ($atomic_type instanceof Atomic\TTemplateParam + if ($atomic_type instanceof TTemplateParam && isset($template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class]) ) { return self::handleTemplateParamStandin( @@ -174,7 +193,7 @@ private static function handleAtomicStandin( ); } - if ($atomic_type instanceof Atomic\TTemplateParamClass + if ($atomic_type instanceof TTemplateParamClass && isset($template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class]) ) { if ($replace) { @@ -197,7 +216,7 @@ private static function handleAtomicStandin( } } - if ($atomic_type instanceof Atomic\TTemplateIndexedAccess) { + if ($atomic_type instanceof TTemplateIndexedAccess) { if ($replace) { $atomic_types = []; @@ -222,9 +241,9 @@ private static function handleAtomicStandin( $array_template_type = $array_template_type->getSingleAtomic(); $offset_template_type = $offset_template_type->getSingleAtomic(); - if ($array_template_type instanceof Atomic\TKeyedArray - && ($offset_template_type instanceof Atomic\TLiteralString - || $offset_template_type instanceof Atomic\TLiteralInt) + if ($array_template_type instanceof TKeyedArray + && ($offset_template_type instanceof TLiteralString + || $offset_template_type instanceof TLiteralInt) && isset($array_template_type->properties[$offset_template_type->value]) ) { $include_first = false; @@ -249,7 +268,7 @@ private static function handleAtomicStandin( return [$atomic_type]; } - if ($atomic_type instanceof Atomic\TTemplateKeyOf) { + if ($atomic_type instanceof TTemplateKeyOf) { if ($replace) { $atomic_types = []; @@ -262,13 +281,13 @@ private static function handleAtomicStandin( if ($template_type->isSingle()) { $template_type = $template_type->getSingleAtomic(); - if ($template_type instanceof Atomic\TKeyedArray - || $template_type instanceof Atomic\TArray - || $template_type instanceof Atomic\TList + if ($template_type instanceof TKeyedArray + || $template_type instanceof TArray + || $template_type instanceof TList ) { - if ($template_type instanceof Atomic\TKeyedArray) { + if ($template_type instanceof TKeyedArray) { $key_type = $template_type->getGenericKeyType(); - } elseif ($template_type instanceof Atomic\TList) { + } elseif ($template_type instanceof TList) { $key_type = Type::getInt(); } else { $key_type = clone $template_type->type_params[0]; @@ -371,26 +390,26 @@ private static function findMatchingAtomicTypesForTemplate( continue; } - if ($atomic_input_type instanceof Atomic\TClosure && $base_type instanceof Atomic\TClosure) { + if ($atomic_input_type instanceof TClosure && $base_type instanceof TClosure) { $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type; continue; } - if ($atomic_input_type instanceof Atomic\TCallable - && $base_type instanceof Atomic\TCallable + if ($atomic_input_type instanceof TCallable + && $base_type instanceof TCallable ) { $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type; continue; } - if ($atomic_input_type instanceof Atomic\TClosure && $base_type instanceof Atomic\TCallable) { + if ($atomic_input_type instanceof TClosure && $base_type instanceof TCallable) { $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type; continue; } - if (($atomic_input_type instanceof Atomic\TArray - || $atomic_input_type instanceof Atomic\TKeyedArray - || $atomic_input_type instanceof Atomic\TList) + if (($atomic_input_type instanceof TArray + || $atomic_input_type instanceof TKeyedArray + || $atomic_input_type instanceof TList) && $key === 'iterable' ) { $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type; @@ -402,8 +421,8 @@ private static function findMatchingAtomicTypesForTemplate( continue; } - if ($atomic_input_type instanceof Atomic\TLiteralClassString - && $base_type instanceof Atomic\TClassString + if ($atomic_input_type instanceof TLiteralClassString + && $base_type instanceof TClassString && $base_type->as_type ) { try { @@ -411,9 +430,9 @@ private static function findMatchingAtomicTypesForTemplate( $codebase->classlike_storage_provider->get($atomic_input_type->value); if (!empty($classlike_storage->template_extended_params[$base_type->as_type->value])) { - $atomic_input_type = new Atomic\TClassString( + $atomic_input_type = new TClassString( $base_type->as_type->value, - new Atomic\TGenericObject( + new TGenericObject( $base_type->as_type->value, array_values($classlike_storage->template_extended_params[$base_type->as_type->value]) ) @@ -427,7 +446,7 @@ private static function findMatchingAtomicTypesForTemplate( } } - if ($base_type instanceof Atomic\TCallable) { + if ($base_type instanceof TCallable) { $matching_atomic_type = CallableTypeComparator::getCallableFromAtomic( $codebase, $atomic_input_type, @@ -441,17 +460,17 @@ private static function findMatchingAtomicTypesForTemplate( } } - if ($atomic_input_type instanceof Atomic\TNamedObject - && ($base_type instanceof Atomic\TNamedObject - || $base_type instanceof Atomic\TIterable) + if ($atomic_input_type instanceof TNamedObject + && ($base_type instanceof TNamedObject + || $base_type instanceof TIterable) ) { - if ($base_type instanceof Atomic\TIterable) { + if ($base_type instanceof TIterable) { if ($atomic_input_type->value === 'Traversable') { $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type; continue; } - $base_type = new Atomic\TGenericObject( + $base_type = new TGenericObject( 'Traversable', $base_type->type_params ); @@ -461,7 +480,7 @@ private static function findMatchingAtomicTypesForTemplate( $classlike_storage = $codebase->classlike_storage_provider->get($atomic_input_type->value); - if ($atomic_input_type instanceof Atomic\TGenericObject + if ($atomic_input_type instanceof TGenericObject && isset($classlike_storage->template_extended_params[$base_type->value]) ) { $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type; @@ -469,7 +488,7 @@ private static function findMatchingAtomicTypesForTemplate( } if (!empty($classlike_storage->template_extended_params[$base_type->value])) { - $atomic_input_type = new Atomic\TGenericObject( + $atomic_input_type = new TGenericObject( $atomic_input_type->value, array_values($classlike_storage->template_extended_params[$base_type->value]) ); @@ -489,7 +508,7 @@ private static function findMatchingAtomicTypesForTemplate( } } - if ($atomic_input_type instanceof Atomic\TTemplateParam) { + if ($atomic_input_type instanceof TTemplateParam) { $matching_atomic_types = array_merge( $matching_atomic_types, self::findMatchingAtomicTypesForTemplate( @@ -511,7 +530,7 @@ private static function findMatchingAtomicTypesForTemplate( * @return list */ private static function handleTemplateParamStandin( - Atomic\TTemplateParam $atomic_type, + TTemplateParam $atomic_type, string $key, ?Union $input_type, ?int $input_arg_offset, @@ -568,10 +587,10 @@ private static function handleTemplateParamStandin( if ($extra_type->isSingle()) { $extra_type = $extra_type->getSingleAtomic(); - if ($extra_type instanceof Atomic\TNamedObject - || $extra_type instanceof Atomic\TTemplateParam - || $extra_type instanceof Atomic\TIterable - || $extra_type instanceof Atomic\TObjectWithProperties + if ($extra_type instanceof TNamedObject + || $extra_type instanceof TTemplateParam + || $extra_type instanceof TIterable + || $extra_type instanceof TObjectWithProperties ) { $extra_types[$extra_type->getKey()] = $extra_type; } @@ -620,7 +639,7 @@ private static function handleTemplateParamStandin( $replacements_found = false; // @codingStandardsIgnoreStart - if ($replacement_atomic_type instanceof Atomic\TTemplateKeyOf + if ($replacement_atomic_type instanceof TTemplateKeyOf && isset($template_result->template_types[$replacement_atomic_type->param_name][$replacement_atomic_type->defining_class]) && count($template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class]) === 1 @@ -631,13 +650,13 @@ private static function handleTemplateParamStandin( $keyed_template = $keyed_template->getSingleAtomic(); } - if ($keyed_template instanceof Atomic\TKeyedArray - || $keyed_template instanceof Atomic\TArray - || $keyed_template instanceof Atomic\TList + if ($keyed_template instanceof TKeyedArray + || $keyed_template instanceof TArray + || $keyed_template instanceof TList ) { - if ($keyed_template instanceof Atomic\TKeyedArray) { + if ($keyed_template instanceof TKeyedArray) { $key_type = $keyed_template->getGenericKeyType(); - } elseif ($keyed_template instanceof Atomic\TList) { + } elseif ($keyed_template instanceof TList) { $key_type = \Psalm\Type::getInt(); } else { $key_type = $keyed_template->type_params[0]; @@ -655,7 +674,7 @@ private static function handleTemplateParamStandin( } } - if ($replacement_atomic_type instanceof Atomic\TTemplateParam + if ($replacement_atomic_type instanceof TTemplateParam && $replacement_atomic_type->defining_class !== $calling_class && $replacement_atomic_type->defining_class !== 'fn-' . $calling_function ) { @@ -781,13 +800,13 @@ private static function handleTemplateParamStandin( } foreach ($atomic_types as &$atomic_type) { - if ($atomic_type instanceof Atomic\TNamedObject - || $atomic_type instanceof Atomic\TTemplateParam - || $atomic_type instanceof Atomic\TIterable - || $atomic_type instanceof Atomic\TObjectWithProperties + if ($atomic_type instanceof TNamedObject + || $atomic_type instanceof TTemplateParam + || $atomic_type instanceof TIterable + || $atomic_type instanceof TObjectWithProperties ) { $atomic_type->extra_types = $extra_types; - } elseif ($atomic_type instanceof Atomic\TObject && $extra_types) { + } elseif ($atomic_type instanceof TObject && $extra_types) { $atomic_type = reset($extra_types); $atomic_type->extra_types = array_slice($extra_types, 1); } @@ -863,10 +882,10 @@ private static function handleTemplateParamStandin( } /** - * @return non-empty-list + * @return non-empty-list */ public static function handleTemplateParamClassStandin( - Atomic\TTemplateParamClass $atomic_type, + TTemplateParamClass $atomic_type, ?Union $input_type, ?int $input_arg_offset, ?string $calling_class, @@ -891,12 +910,12 @@ public static function handleTemplateParamClassStandin( $valid_input_atomic_types = []; foreach ($input_type->getAtomicTypes() as $input_atomic_type) { - if ($input_atomic_type instanceof Atomic\TLiteralClassString) { - $valid_input_atomic_types[] = new Atomic\TNamedObject( + if ($input_atomic_type instanceof TLiteralClassString) { + $valid_input_atomic_types[] = new TNamedObject( $input_atomic_type->value ); - } elseif ($input_atomic_type instanceof Atomic\TTemplateParamClass) { - $valid_input_atomic_types[] = new Atomic\TTemplateParam( + } elseif ($input_atomic_type instanceof TTemplateParamClass) { + $valid_input_atomic_types[] = new TTemplateParam( $input_atomic_type->param_name, $input_atomic_type->as_type ? new Union([$input_atomic_type->as_type]) @@ -905,18 +924,18 @@ public static function handleTemplateParamClassStandin( : Type::getMixed()), $input_atomic_type->defining_class ); - } elseif ($input_atomic_type instanceof Atomic\TClassString) { + } elseif ($input_atomic_type instanceof TClassString) { if ($input_atomic_type->as_type) { $valid_input_atomic_types[] = clone $input_atomic_type->as_type; } elseif ($input_atomic_type->as !== 'object') { - $valid_input_atomic_types[] = new Atomic\TNamedObject( + $valid_input_atomic_types[] = new TNamedObject( $input_atomic_type->as ); } else { - $valid_input_atomic_types[] = new Atomic\TObject(); + $valid_input_atomic_types[] = new TObject(); } - } elseif ($input_atomic_type instanceof Atomic\TDependentGetClass) { - $valid_input_atomic_types[] = new Atomic\TObject(); + } elseif ($input_atomic_type instanceof TDependentGetClass) { + $valid_input_atomic_types[] = new TObject(); } } @@ -949,7 +968,7 @@ public static function handleTemplateParamClassStandin( $first = $as_type_union->getSingleAtomic(); - if (count($as_type_union->getAtomicTypes()) === 1 && $first instanceof Atomic\TNamedObject) { + if (count($as_type_union->getAtomicTypes()) === 1 && $first instanceof TNamedObject) { $atomic_type->as_type = $first; } else { $atomic_type->as_type = null; @@ -986,18 +1005,18 @@ public static function handleTemplateParamClassStandin( [$atomic_type->defining_class]; foreach ($template_type->getAtomicTypes() as $template_atomic_type) { - if ($template_atomic_type instanceof Atomic\TNamedObject) { - $atomic_types[] = new Atomic\TClassString( + if ($template_atomic_type instanceof TNamedObject) { + $atomic_types[] = new TClassString( $template_atomic_type->value, $template_atomic_type ); - } elseif ($template_atomic_type instanceof Atomic\TObject) { - $atomic_types[] = new Atomic\TClassString(); + } elseif ($template_atomic_type instanceof TObject) { + $atomic_types[] = new TClassString(); } } } - $class_string = new Atomic\TClassString($atomic_type->as, $atomic_type->as_type); + $class_string = new TClassString($atomic_type->as, $atomic_type->as_type); if (!$atomic_types) { $atomic_types[] = $class_string; @@ -1029,7 +1048,7 @@ public static function getRootTemplateType( $mapped_type_atomic_types = array_values($mapped_type->getAtomicTypes()); if (count($mapped_type_atomic_types) > 1 - || !$mapped_type_atomic_types[0] instanceof Atomic\TTemplateParam + || !$mapped_type_atomic_types[0] instanceof TTemplateParam ) { return $mapped_type; } @@ -1106,8 +1125,8 @@ function (TemplateBound $bound_a, TemplateBound $bound_b) { } /** - * @param Atomic\TGenericObject|Atomic\TIterable $input_type_part - * @param Atomic\TGenericObject|Atomic\TIterable $container_type_part + * @param TGenericObject|TIterable $input_type_part + * @param TGenericObject|TIterable $container_type_part * @return list */ public static function getMappedGenericTypeParams( @@ -1135,8 +1154,8 @@ public static function getMappedGenericTypeParams( $replacement_templates = []; if ($input_template_types - && (!$input_type_part instanceof Atomic\TGenericObject || !$input_type_part->remapped_params) - && (!$container_type_part instanceof Atomic\TGenericObject || !$container_type_part->remapped_params) + && (!$input_type_part instanceof TGenericObject || !$input_type_part->remapped_params) + && (!$container_type_part instanceof TGenericObject || !$container_type_part->remapped_params) ) { foreach ($input_template_types as $template_name => $_) { if (!isset($input_type_params[$i])) { @@ -1160,7 +1179,7 @@ public static function getMappedGenericTypeParams( $new_input_param = null; foreach ($extended_input_param_type->getAtomicTypes() as $et) { - if ($et instanceof Atomic\TTemplateParam) { + if ($et instanceof TTemplateParam) { $ets = Methods::getExtendedTemplatedTypes( $et, $template_extends @@ -1170,7 +1189,7 @@ public static function getMappedGenericTypeParams( } if ($ets - && $ets[0] instanceof Atomic\TTemplateParam + && $ets[0] instanceof TTemplateParam && isset( $input_class_storage->template_types [$ets[0]->param_name] diff --git a/src/Psalm/Internal/Type/TypeAlias.php b/src/Psalm/Internal/Type/TypeAlias.php index 2e27ad134a2..a8c05fb6d30 100644 --- a/src/Psalm/Internal/Type/TypeAlias.php +++ b/src/Psalm/Internal/Type/TypeAlias.php @@ -1,4 +1,5 @@ */ public $value_types = []; - /** @var array|null */ + /** @var array|null */ public $named_object_types = []; /** @var list */ @@ -57,20 +65,20 @@ class TypeCombination /** @var ?bool */ public $mixed_from_loop_isset; - /** @var array|null */ + /** @var array|null */ public $strings = []; - /** @var array|null */ + /** @var array|null */ public $ints = []; - /** @var array|null */ + /** @var array|null */ public $floats = []; - /** @var array|null */ + /** @var array|null */ public $class_string_types = []; /** - * @var array|null + * @var array|null */ public $extra_types; @@ -86,6 +94,6 @@ class TypeCombination /** @var array */ public $class_string_map_names = []; - /** @var array */ + /** @var array */ public $class_string_map_as_types = []; } diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index b308759ab05..4cdc543fc39 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -16,6 +16,7 @@ use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TCallableString; use Psalm\Type\Atomic\TClassString; +use Psalm\Type\Atomic\TClassStringMap; use Psalm\Type\Atomic\TEmpty; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TFalse; @@ -46,10 +47,12 @@ use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TNumericString; use Psalm\Type\Atomic\TObject; +use Psalm\Type\Atomic\TObjectWithProperties; use Psalm\Type\Atomic\TPositiveInt; use Psalm\Type\Atomic\TScalar; use Psalm\Type\Atomic\TString; use Psalm\Type\Atomic\TTemplateParam; +use Psalm\Type\Atomic\TTemplateParamClass; use Psalm\Type\Atomic\TTraitString; use Psalm\Type\Atomic\TTrue; use Psalm\Type\Union; @@ -286,7 +289,7 @@ public static function combine( } $has_non_specific_string = isset($combination->value_types['string']) - && get_class($combination->value_types['string']) === Type\Atomic\TString::class; + && get_class($combination->value_types['string']) === TString::class; if (!$has_non_specific_string) { $object_type = self::combine( @@ -501,7 +504,7 @@ private static function scrapeTypeProperties( if ($type instanceof TNamedObject || $type instanceof TTemplateParam || $type instanceof TIterable - || $type instanceof Type\Atomic\TObjectWithProperties + || $type instanceof TObjectWithProperties ) { if ($type->extra_types) { $combination->extra_types = array_merge( @@ -601,7 +604,7 @@ private static function scrapeTypeProperties( return null; } - if ($type instanceof Atomic\TClassStringMap) { + if ($type instanceof TClassStringMap) { foreach ([$type->getStandinKeyParam(), $type->value_param] as $i => $type_param) { /** @psalm-suppress PropertyTypeCoercion */ $combination->array_type_params[$i] = Type::combineUnionTypes( @@ -924,9 +927,9 @@ private static function scrapeStringProperties( return; } - if ($type instanceof Type\Atomic\TTemplateParamClass) { + if ($type instanceof TTemplateParamClass) { $combination->value_types[$type_key] = $type; - } elseif ($type instanceof Type\Atomic\TClassString) { + } elseif ($type instanceof TClassString) { if (!$type->as_type) { $combination->class_string_types['object'] = new TObject(); } else { @@ -968,17 +971,17 @@ private static function scrapeStringProperties( ) { // do nothing } elseif (isset($combination->value_types['string']) - && $combination->value_types['string'] instanceof Type\Atomic\TLowercaseString + && $combination->value_types['string'] instanceof TLowercaseString && strtolower($type->value) === $type->value ) { // do nothing } elseif (isset($combination->value_types['string']) - && $combination->value_types['string'] instanceof Type\Atomic\TNonFalsyString + && $combination->value_types['string'] instanceof TNonFalsyString && $type->value ) { // do nothing } elseif (isset($combination->value_types['string']) - && $combination->value_types['string'] instanceof Type\Atomic\TNonEmptyString + && $combination->value_types['string'] instanceof TNonEmptyString && $type->value !== '' ) { // do nothing @@ -1008,7 +1011,7 @@ private static function scrapeStringProperties( } $combination->strings = null; - } elseif ($type instanceof Type\Atomic\TLowercaseString) { + } elseif ($type instanceof TLowercaseString) { $has_non_lowercase_string = false; foreach ($combination->strings as $string_type) { @@ -1025,7 +1028,7 @@ private static function scrapeStringProperties( } $combination->strings = null; - } elseif ($type instanceof Type\Atomic\TNonEmptyString) { + } elseif ($type instanceof TNonEmptyString) { $has_empty_string = false; foreach ($combination->strings as $string_type) { @@ -1381,7 +1384,7 @@ private static function handleKeyedArrayEntries( ) { $combination->objectlike_entries = array_filter( $combination->objectlike_entries, - function (Type\Union $type): bool { + function (Union $type): bool { return !$type->possibly_undefined; } ); @@ -1420,7 +1423,7 @@ function (Type\Union $type): bool { $new_types[] = $objectlike; } else { - $new_types[] = new Type\Atomic\TArray([Type::getArrayKey(), Type::getMixed()]); + $new_types[] = new TArray([Type::getArrayKey(), Type::getMixed()]); } // if we're merging an empty array with an object-like, clobber empty array @@ -1474,7 +1477,7 @@ private static function getArrayTypeFromGenericParams( $objectlike_generic_type->possibly_undefined = false; - $objectlike_key_type = new Type\Union(array_values($objectlike_keys)); + $objectlike_key_type = new Union(array_values($objectlike_keys)); $objectlike_key_type = Type::combineUnionTypes( $combination->objectlike_key_type, @@ -1537,7 +1540,7 @@ private static function getArrayTypeFromGenericParams( && count($combination->class_string_map_as_types) === 1 && count($combination->class_string_map_names) === 1 ) { - $array_type = new Type\Atomic\TClassStringMap( + $array_type = new TClassStringMap( array_keys($combination->class_string_map_names)[0], array_values($combination->class_string_map_as_types)[0], $generic_type_params[1] diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index e1b31c29eed..5fc1fd2519e 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -1,4 +1,5 @@ from_docblock = $return_type->from_docblock; @@ -102,13 +124,13 @@ public static function expandUnion( } /** - * @param string|Type\Atomic\TNamedObject|Type\Atomic\TTemplateParam|null $static_class_type + * @param string|TNamedObject|TTemplateParam|null $static_class_type * - * @return Type\Atomic|non-empty-list + * @return Atomic|non-empty-list */ public static function expandAtomic( Codebase $codebase, - Type\Atomic &$return_type, + Atomic &$return_type, ?string $self_class, $static_class_type, ?string $parent_class, @@ -164,7 +186,7 @@ public static function expandAtomic( } } - if ($return_type instanceof Type\Atomic\TClassString + if ($return_type instanceof TClassString && $return_type->as_type ) { $new_as_type = clone $return_type->as_type; @@ -186,7 +208,7 @@ public static function expandAtomic( $return_type->as_type = $new_as_type; $return_type->as = $return_type->as_type->value; } - } elseif ($return_type instanceof Type\Atomic\TTemplateParam) { + } elseif ($return_type instanceof TTemplateParam) { $new_as_type = self::expandUnion( $codebase, clone $return_type->as, @@ -207,7 +229,7 @@ public static function expandAtomic( $return_type->as = $new_as_type; } - if ($return_type instanceof Type\Atomic\TClassConstant) { + if ($return_type instanceof TClassConstant) { if ($return_type->fq_classlike_name === 'self' && $self_class) { $return_type->fq_classlike_name = $self_class; } @@ -218,7 +240,7 @@ public static function expandAtomic( if ($evaluate_class_constants && $codebase->classOrInterfaceOrEnumExists($return_type->fq_classlike_name)) { if (strtolower($return_type->const_name) === 'class') { - return new Type\Atomic\TLiteralClassString($return_type->fq_classlike_name); + return new TLiteralClassString($return_type->fq_classlike_name); } $class_storage = $codebase->classlike_storage_provider->get($return_type->fq_classlike_name); @@ -277,7 +299,7 @@ function ($constant_name) use ($const_name_part): bool { return $return_type; } - if ($return_type instanceof Type\Atomic\TTypeAlias) { + if ($return_type instanceof TTypeAlias) { $declaring_fq_classlike_name = $return_type->declaring_fq_classlike_name; if ($declaring_fq_classlike_name === 'self' && $self_class) { @@ -329,8 +351,8 @@ function ($constant_name) use ($const_name_part): bool { return $return_type; } - if ($return_type instanceof Type\Atomic\TKeyOfClassConstant - || $return_type instanceof Type\Atomic\TValueOfClassConstant + if ($return_type instanceof TKeyOfClassConstant + || $return_type instanceof TValueOfClassConstant ) { if ($return_type->fq_classlike_name === 'self' && $self_class) { $return_type->fq_classlike_name = $self_class; @@ -349,14 +371,14 @@ function ($constant_name) use ($const_name_part): bool { if ($class_constant_type) { foreach ($class_constant_type->getAtomicTypes() as $const_type_atomic) { - if ($const_type_atomic instanceof Type\Atomic\TKeyedArray - || $const_type_atomic instanceof Type\Atomic\TArray + if ($const_type_atomic instanceof TKeyedArray + || $const_type_atomic instanceof TArray ) { - if ($const_type_atomic instanceof Type\Atomic\TKeyedArray) { + if ($const_type_atomic instanceof TKeyedArray) { $const_type_atomic = $const_type_atomic->getGenericArrayType(); } - if ($return_type instanceof Type\Atomic\TKeyOfClassConstant) { + if ($return_type instanceof TKeyOfClassConstant) { return array_values($const_type_atomic->type_params[0]->getAtomicTypes()); } @@ -369,9 +391,9 @@ function ($constant_name) use ($const_name_part): bool { return $return_type; } - if ($return_type instanceof Type\Atomic\TIntMask) { + if ($return_type instanceof TIntMask) { if (!$evaluate_class_constants) { - return new Type\Atomic\TInt(); + return new TInt(); } $potential_ints = []; @@ -394,8 +416,8 @@ function ($constant_name) use ($const_name_part): bool { $new_value_type = reset($new_value_type); } - if (!$new_value_type instanceof Type\Atomic\TLiteralInt) { - return new Type\Atomic\TInt(); + if (!$new_value_type instanceof TLiteralInt) { + return new TInt(); } $potential_ints[] = $new_value_type->value; @@ -404,9 +426,9 @@ function ($constant_name) use ($const_name_part): bool { return TypeParser::getComputedIntsFromMask($potential_ints); } - if ($return_type instanceof Type\Atomic\TIntMaskOf) { + if ($return_type instanceof TIntMaskOf) { if (!$evaluate_class_constants) { - return new Type\Atomic\TInt(); + return new TInt(); } $value_type = $return_type->value; @@ -425,14 +447,14 @@ function ($constant_name) use ($const_name_part): bool { ); if (!is_array($new_value_types)) { - return new Type\Atomic\TInt(); + return new TInt(); } $potential_ints = []; foreach ($new_value_types as $new_value_type) { - if (!$new_value_type instanceof Type\Atomic\TLiteralInt) { - return new Type\Atomic\TInt(); + if (!$new_value_type instanceof TLiteralInt) { + return new TInt(); } $potential_ints[] = $new_value_type->value; @@ -441,9 +463,9 @@ function ($constant_name) use ($const_name_part): bool { return TypeParser::getComputedIntsFromMask($potential_ints); } - if ($return_type instanceof Type\Atomic\TArray - || $return_type instanceof Type\Atomic\TGenericObject - || $return_type instanceof Type\Atomic\TIterable + if ($return_type instanceof TArray + || $return_type instanceof TGenericObject + || $return_type instanceof TIterable ) { foreach ($return_type->type_params as $k => $type_param) { /** @psalm-suppress PropertyTypeCoercion */ @@ -460,7 +482,7 @@ function ($constant_name) use ($const_name_part): bool { $expand_templates ); } - } elseif ($return_type instanceof Type\Atomic\TKeyedArray) { + } elseif ($return_type instanceof TKeyedArray) { foreach ($return_type->properties as &$property_type) { $property_type = self::expandUnion( $codebase, @@ -475,7 +497,7 @@ function ($constant_name) use ($const_name_part): bool { $expand_templates ); } - } elseif ($return_type instanceof Type\Atomic\TList) { + } elseif ($return_type instanceof TList) { $return_type->type_param = self::expandUnion( $codebase, $return_type->type_param, @@ -490,7 +512,7 @@ function ($constant_name) use ($const_name_part): bool { ); } - if ($return_type instanceof Type\Atomic\TObjectWithProperties) { + if ($return_type instanceof TObjectWithProperties) { foreach ($return_type->properties as &$property_type) { $property_type = self::expandUnion( $codebase, @@ -507,8 +529,8 @@ function ($constant_name) use ($const_name_part): bool { } } - if ($return_type instanceof Type\Atomic\TCallable - || $return_type instanceof Type\Atomic\TClosure + if ($return_type instanceof TCallable + || $return_type instanceof TClosure ) { if ($return_type->params) { foreach ($return_type->params as $param) { @@ -544,7 +566,7 @@ function ($constant_name) use ($const_name_part): bool { } } - if ($return_type instanceof Type\Atomic\TConditional) { + if ($return_type instanceof TConditional) { return self::expandConditional( $codebase, $return_type, @@ -563,12 +585,12 @@ function ($constant_name) use ($const_name_part): bool { } /** - * @param string|Type\Atomic\TNamedObject|Type\Atomic\TTemplateParam|null $static_class_type - * @return Type\Atomic\TNamedObject|Type\Atomic\TTemplateParam + * @param string|TNamedObject|TTemplateParam|null $static_class_type + * @return TNamedObject|TTemplateParam */ private static function expandNamedObject( Codebase $codebase, - Type\Atomic\TNamedObject $return_type, + TNamedObject $return_type, ?string $self_class, $static_class_type, ?string $parent_class, @@ -593,7 +615,7 @@ function ($type_map) { } ) ) { - $return_type = new Type\Atomic\TGenericObject( + $return_type = new TGenericObject( $return_type->value, array_values( array_map( @@ -616,8 +638,8 @@ function ($type_map) { if (is_string($static_class_type)) { $return_type->value = $static_class_type; } else { - if ($return_type instanceof Type\Atomic\TGenericObject - && $static_class_type instanceof Type\Atomic\TGenericObject + if ($return_type instanceof TGenericObject + && $static_class_type instanceof TGenericObject ) { $return_type->value = $static_class_type->value; } else { @@ -629,8 +651,8 @@ function ($type_map) { $return_type->was_static = true; } } elseif ($return_type->was_static - && ($static_class_type instanceof Type\Atomic\TNamedObject - || $static_class_type instanceof Type\Atomic\TTemplateParam) + && ($static_class_type instanceof TNamedObject + || $static_class_type instanceof TTemplateParam) ) { $return_type = clone $return_type; $cloned_static = clone $static_class_type; @@ -661,13 +683,13 @@ function ($type_map) { } /** - * @param string|Type\Atomic\TNamedObject|Type\Atomic\TTemplateParam|null $static_class_type + * @param string|TNamedObject|TTemplateParam|null $static_class_type * - * @return Type\Atomic|non-empty-list + * @return Atomic|non-empty-list */ private static function expandConditional( Codebase $codebase, - Type\Atomic\TConditional $return_type, + TConditional $return_type, ?string $self_class, $static_class_type, ?string $parent_class, @@ -821,12 +843,12 @@ static function (Atomic $atomic_type): bool { $all_conditional_return_types = array_filter( $all_conditional_return_types, static function (Atomic $atomic_type): bool { - return !$atomic_type instanceof Atomic\TVoid; + return !$atomic_type instanceof TVoid; } ); if (count($all_conditional_return_types) !== $number_of_types) { - $null_type = new Type\Atomic\TNull(); + $null_type = new TNull(); $null_type->from_docblock = true; $all_conditional_return_types[] = $null_type; } diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index 8ec36b513f3..20c9308b2bd 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -1,4 +1,5 @@ children[0], $codebase, @@ -184,11 +214,11 @@ public static function getTypeFromTree( return $callable_type; } - if ($parse_tree instanceof ParseTree\CallableTree) { + if ($parse_tree instanceof CallableTree) { return self::getTypeFromCallableTree($parse_tree, $codebase, $template_type_map, $type_aliases); } - if ($parse_tree instanceof ParseTree\EncapsulationTree) { + if ($parse_tree instanceof EncapsulationTree) { if (!$parse_tree->terminated) { throw new TypeParseTreeException('Unterminated parentheses'); } @@ -206,7 +236,7 @@ public static function getTypeFromTree( ); } - if ($parse_tree instanceof ParseTree\NullableTree) { + if ($parse_tree instanceof NullableTree) { if (!isset($parse_tree->children[0])) { throw new TypeParseTreeException('Misplaced question mark'); } @@ -231,25 +261,25 @@ public static function getTypeFromTree( ]); } - if ($parse_tree instanceof ParseTree\MethodTree - || $parse_tree instanceof ParseTree\MethodWithReturnTypeTree + if ($parse_tree instanceof MethodTree + || $parse_tree instanceof MethodWithReturnTypeTree ) { throw new TypeParseTreeException('Misplaced brackets'); } - if ($parse_tree instanceof ParseTree\IndexedAccessTree) { + if ($parse_tree instanceof IndexedAccessTree) { return self::getTypeFromIndexAccessTree($parse_tree, $template_type_map); } - if ($parse_tree instanceof ParseTree\TemplateAsTree) { - return new Atomic\TTemplateParam( + if ($parse_tree instanceof TemplateAsTree) { + return new TTemplateParam( $parse_tree->param_name, new Union([new TNamedObject($parse_tree->as)]), 'class-string-map' ); } - if ($parse_tree instanceof ParseTree\ConditionalTree) { + if ($parse_tree instanceof ConditionalTree) { $template_param_name = $parse_tree->condition->param_name; if (!isset($template_type_map[$template_param_name])) { @@ -298,7 +328,7 @@ public static function getTypeFromTree( $else_type = new Union([$else_type]); } - return new Atomic\TConditional( + return new TConditional( $template_param_name, $first_class, $template_type_map[$template_param_name][$first_class], @@ -308,7 +338,7 @@ public static function getTypeFromTree( ); } - if (!$parse_tree instanceof ParseTree\Value) { + if (!$parse_tree instanceof Value) { throw new InvalidArgumentException('Unrecognised parse tree type ' . get_class($parse_tree)); } @@ -330,10 +360,10 @@ public static function getTypeFromTree( } if ($const_name === 'class') { - return new Atomic\TLiteralClassString($fq_classlike_name); + return new TLiteralClassString($fq_classlike_name); } - return new Atomic\TClassConstant($fq_classlike_name, $const_name); + return new TClassConstant($fq_classlike_name, $const_name); } if (preg_match('/^\-?(0|[1-9][0-9]*)(\.[0-9]{1,})$/', $parse_tree->value)) { @@ -363,9 +393,9 @@ private static function getGenericParamClass( string $param_name, Union $as, string $defining_class - ): Atomic\TTemplateParamClass { + ): TTemplateParamClass { if ($as->hasMixed()) { - return new Atomic\TTemplateParamClass( + return new TTemplateParamClass( $param_name, 'object', null, @@ -381,7 +411,7 @@ private static function getGenericParamClass( foreach ($as->getAtomicTypes() as $t) { if ($t instanceof TObject) { - return new Atomic\TTemplateParamClass( + return new TTemplateParamClass( $param_name, 'object', null, @@ -397,7 +427,7 @@ private static function getGenericParamClass( $as->substitute(new Union([$t]), new Union([$traversable])); - return new Atomic\TTemplateParamClass( + return new TTemplateParamClass( $param_name, $traversable->value, $traversable, @@ -405,14 +435,14 @@ private static function getGenericParamClass( ); } - if ($t instanceof Atomic\TTemplateParam) { + if ($t instanceof TTemplateParam) { $t_atomic_type = count($t->as->getAtomicTypes()) === 1 ? $t->as->getSingleAtomic() : null; if (!$t_atomic_type instanceof TNamedObject) { $t_atomic_type = null; } - return new Atomic\TTemplateParamClass( + return new TTemplateParamClass( $t->param_name, $t_atomic_type->value ?? 'object', $t_atomic_type, @@ -426,7 +456,7 @@ private static function getGenericParamClass( ); } - return new Atomic\TTemplateParamClass( + return new TTemplateParamClass( $param_name, $t->value, $t, @@ -479,7 +509,7 @@ function ($int) { * @psalm-suppress ComplexMethod to be refactored */ private static function getTypeFromGenericTree( - ParseTree\GenericTree $parse_tree, + GenericTree $parse_tree, Codebase $codebase, array $template_type_map, array $type_aliases @@ -629,7 +659,7 @@ private static function getTypeFromGenericTree( if ($template_marker instanceof TNamedObject) { $template_param_name = $template_marker->value; - } elseif ($template_marker instanceof Atomic\TTemplateParam) { + } elseif ($template_marker instanceof TTemplateParam) { $template_param_name = $template_marker->param_name; $template_as_type = $template_marker->as->getSingleAtomic(); @@ -657,7 +687,7 @@ private static function getTypeFromGenericTree( if (isset($template_type_map[$param_name])) { $defining_class = array_keys($template_type_map[$param_name])[0]; - return new Atomic\TTemplateKeyOf( + return new TTemplateKeyOf( $param_name, $defining_class, $template_type_map[$param_name][$defining_class] @@ -670,13 +700,13 @@ private static function getTypeFromGenericTree( throw new TypeParseTreeException('Union types are not allowed in key-of type'); } - if (!$param_union_types[0] instanceof Atomic\TClassConstant) { + if (!$param_union_types[0] instanceof TClassConstant) { throw new TypeParseTreeException( 'Untemplated key-of param ' . $param_name . ' should be a class constant' ); } - return new Atomic\TKeyOfClassConstant( + return new TKeyOfClassConstant( $param_union_types[0]->fq_classlike_name, $param_union_types[0]->const_name ); @@ -691,13 +721,13 @@ private static function getTypeFromGenericTree( throw new TypeParseTreeException('Union types are not allowed in value-of type'); } - if (!$param_union_types[0] instanceof Atomic\TClassConstant) { + if (!$param_union_types[0] instanceof TClassConstant) { throw new TypeParseTreeException( 'Untemplated value-of param ' . $param_name . ' should be a class constant' ); } - return new Atomic\TValueOfClassConstant( + return new TValueOfClassConstant( $param_union_types[0]->fq_classlike_name, $param_union_types[0]->const_name ); @@ -737,7 +767,7 @@ private static function getTypeFromGenericTree( } if (!$atomic_type instanceof TLiteralInt - && !($atomic_type instanceof Atomic\TClassConstant + && !($atomic_type instanceof TClassConstant && strpos($atomic_type->const_name, '*') === false) ) { throw new TypeParseTreeException( @@ -752,7 +782,7 @@ private static function getTypeFromGenericTree( foreach ($atomic_types as $atomic_type) { if (!$atomic_type instanceof TLiteralInt) { - return new Atomic\TIntMask($atomic_types); + return new TIntMask($atomic_types); } $potential_ints[] = $atomic_type->value; @@ -770,14 +800,14 @@ private static function getTypeFromGenericTree( $param_type = $param_union_types[0]; - if (!$param_type instanceof Atomic\TClassConstant - && !$param_type instanceof Atomic\TValueOfClassConstant - && !$param_type instanceof Atomic\TKeyOfClassConstant + if (!$param_type instanceof TClassConstant + && !$param_type instanceof TValueOfClassConstant + && !$param_type instanceof TKeyOfClassConstant ) { throw new TypeParseTreeException( 'Invalid reference passed to int-mask-of' ); - } elseif ($param_type instanceof Atomic\TClassConstant + } elseif ($param_type instanceof TClassConstant && strpos($param_type->const_name, '*') === false ) { throw new TypeParseTreeException( @@ -785,7 +815,7 @@ private static function getTypeFromGenericTree( ); } - return new Atomic\TIntMaskOf($param_type); + return new TIntMaskOf($param_type); } if ($generic_type_value === 'int') { @@ -821,14 +851,14 @@ private static function getTypeFromGenericTree( } if ($min_bound === null && $max_bound === null) { - return new Atomic\TInt(); + return new TInt(); } if ($min_bound === 1 && $max_bound === null) { - return new Atomic\TPositiveInt(); + return new TPositiveInt(); } - return new Atomic\TIntRange($min_bound, $max_bound); + return new TIntRange($min_bound, $max_bound); } if (isset(TypeTokenizer::PSALM_RESERVED_WORDS[$generic_type_value]) @@ -847,7 +877,7 @@ private static function getTypeFromGenericTree( * @throws TypeParseTreeException */ private static function getTypeFromUnionTree( - ParseTree\UnionTree $parse_tree, + UnionTree $parse_tree, Codebase $codebase, array $template_type_map, array $type_aliases @@ -857,7 +887,7 @@ private static function getTypeFromUnionTree( $atomic_types = []; foreach ($parse_tree->children as $child_tree) { - if ($child_tree instanceof ParseTree\NullableTree) { + if ($child_tree instanceof NullableTree) { if (!isset($child_tree->children[0])) { throw new TypeParseTreeException('Invalid ? character'); } @@ -910,7 +940,7 @@ private static function getTypeFromUnionTree( * @throws TypeParseTreeException */ private static function getTypeFromIntersectionTree( - ParseTree\IntersectionTree $parse_tree, + IntersectionTree $parse_tree, Codebase $codebase, array $template_type_map, array $type_aliases @@ -1072,7 +1102,7 @@ function (ParseTree $child_tree) use ($codebase, $template_type_map, $type_alias * @throws TypeParseTreeException */ private static function getTypeFromCallableTree( - ParseTree\CallableTree $parse_tree, + CallableTree $parse_tree, Codebase $codebase, array $template_type_map, array $type_aliases @@ -1089,7 +1119,7 @@ function (ParseTree $child_tree) use ( $is_variadic = false; $is_optional = false; - if ($child_tree instanceof ParseTree\CallableParamTree) { + if ($child_tree instanceof CallableParamTree) { if (isset($child_tree->children[0])) { $tree_type = self::getTypeFromTree( $child_tree->children[0], @@ -1105,7 +1135,7 @@ function (ParseTree $child_tree) use ( $is_variadic = $child_tree->variadic; $is_optional = $child_tree->has_default; } else { - if ($child_tree instanceof ParseTree\Value && strpos($child_tree->value, '$') > 0) { + if ($child_tree instanceof Value && strpos($child_tree->value, '$') > 0) { $child_tree->value = preg_replace('/(.+)\$.*/', '$1', $child_tree->value); } @@ -1152,10 +1182,10 @@ function (ParseTree $child_tree) use ( * @throws TypeParseTreeException */ private static function getTypeFromIndexAccessTree( - ParseTree\IndexedAccessTree $parse_tree, + IndexedAccessTree $parse_tree, array $template_type_map - ): Atomic\TTemplateIndexedAccess { - if (!isset($parse_tree->children[0]) || !$parse_tree->children[0] instanceof ParseTree\Value) { + ): TTemplateIndexedAccess { + if (!isset($parse_tree->children[0]) || !$parse_tree->children[0] instanceof Value) { throw new TypeParseTreeException('Unrecognised indexed access'); } @@ -1180,7 +1210,7 @@ private static function getTypeFromIndexAccessTree( ) { $offset_template_type = $offset_template_data['']->getSingleAtomic(); - if ($offset_template_type instanceof Atomic\TTemplateKeyOf) { + if ($offset_template_type instanceof TTemplateKeyOf) { $offset_defining_class = $offset_template_type->defining_class; } } @@ -1193,7 +1223,7 @@ private static function getTypeFromIndexAccessTree( throw new TypeParseTreeException('Template params are defined in different locations'); } - return new Atomic\TTemplateIndexedAccess( + return new TTemplateIndexedAccess( $array_param_name, $offset_param_name, $array_defining_class @@ -1203,23 +1233,26 @@ private static function getTypeFromIndexAccessTree( /** * @param array> $template_type_map * @param array $type_aliases - * @return Atomic\TCallableKeyedArray|TKeyedArray|TObjectWithProperties + * @return TCallableKeyedArray|TKeyedArray|TObjectWithProperties * @throws TypeParseTreeException */ private static function getTypeFromKeyedArrayTree( - ParseTree\KeyedArrayTree $parse_tree, + KeyedArrayTree $parse_tree, Codebase $codebase, array $template_type_map, array $type_aliases ) { $properties = []; + $class_strings = []; $type = $parse_tree->value; $is_tuple = true; foreach ($parse_tree->children as $i => $property_branch) { - if (!$property_branch instanceof ParseTree\KeyedArrayPropertyTree) { + $class_string = false; + + if (!$property_branch instanceof KeyedArrayPropertyTree) { $property_type = self::getTypeFromTree( $property_branch, $codebase, @@ -1238,7 +1271,17 @@ private static function getTypeFromKeyedArrayTree( $type_aliases ); $property_maybe_undefined = $property_branch->possibly_undefined; - $property_key = $property_branch->value; + if (strpos($property_branch->value, '::')) { + [$fq_classlike_name, $const_name] = explode('::', $property_branch->value); + if ($const_name === 'class') { + $property_key = $fq_classlike_name; + $class_string = true; + } else { + $property_key = $property_branch->value; + } + } else { + $property_key = $property_branch->value; + } $is_tuple = false; } else { throw new TypeParseTreeException( @@ -1259,6 +1302,9 @@ private static function getTypeFromKeyedArrayTree( } $properties[$property_key] = $property_type; + if ($class_string) { + $class_strings[$property_key] = true; + } } if ($type !== 'array' && $type !== 'object' && $type !== 'callable-array') { @@ -1274,10 +1320,10 @@ private static function getTypeFromKeyedArrayTree( } if ($type === 'callable-array') { - return new Atomic\TCallableKeyedArray($properties); + return new TCallableKeyedArray($properties); } - $object_like = new TKeyedArray($properties); + $object_like = new TKeyedArray($properties, $class_strings); if ($is_tuple) { $object_like->sealed = true; diff --git a/src/Psalm/Internal/Type/TypeTokenizer.php b/src/Psalm/Internal/Type/TypeTokenizer.php index e893fd06f0e..020a3b270ca 100644 --- a/src/Psalm/Internal/Type/TypeTokenizer.php +++ b/src/Psalm/Internal/Type/TypeTokenizer.php @@ -1,8 +1,10 @@ replacement_tokens; array_unshift($replacement_tokens, ['(', $i]); diff --git a/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php b/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php index bde8233b5f7..94d55b17ac7 100644 --- a/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php +++ b/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php @@ -1,4 +1,5 @@ source->getCodebase(); - if ($this->code_location instanceof CodeLocation\DocblockTypeLocation + if ($this->code_location instanceof DocblockTypeLocation && $codebase->store_node_types && $atomic->offset_start !== null && $atomic->offset_end !== null diff --git a/src/Psalm/Internal/TypeVisitor/TypeScanner.php b/src/Psalm/Internal/TypeVisitor/TypeScanner.php index e5e0e3aae3a..3e742886991 100644 --- a/src/Psalm/Internal/TypeVisitor/TypeScanner.php +++ b/src/Psalm/Internal/TypeVisitor/TypeScanner.php @@ -1,4 +1,5 @@ getMixedOriginMessage() : $e->message); - throw new Exception\CodeException( + throw new CodeException( $issue_type . ' - ' . $e->getShortLocationWithPrevious() . ':' . $e->code_location->getColumn() @@ -478,7 +481,7 @@ public static function processUnusedSuppressions(FileProvider $file_provider): v self::add( new UnusedPsalmSuppress( 'This suppression is never used', - new CodeLocation\Raw( + new Raw( $file_contents, $file_path, $config->shortenFileName($file_path), diff --git a/src/Psalm/Node/Expr/AssignOp/VirtualBitwiseAnd.php b/src/Psalm/Node/Expr/AssignOp/VirtualBitwiseAnd.php index 5873c0e8421..9c28c1e74ea 100644 --- a/src/Psalm/Node/Expr/AssignOp/VirtualBitwiseAnd.php +++ b/src/Psalm/Node/Expr/AssignOp/VirtualBitwiseAnd.php @@ -1,4 +1,6 @@ - $template_type_parameters + * @param ?array $template_type_parameters * @param lowercase-string $method_name_lowercase * @param lowercase-string $called_method_name_lowercase */ @@ -117,7 +116,7 @@ public function getCodeLocation(): CodeLocation } /** - * @return Type\Union[]|null + * @return Union[]|null */ public function getTemplateTypeParameters(): ?array { diff --git a/src/Psalm/Plugin/EventHandler/Event/MethodVisibilityProviderEvent.php b/src/Psalm/Plugin/EventHandler/Event/MethodVisibilityProviderEvent.php index d52919f04dd..90ceb7d36be 100644 --- a/src/Psalm/Plugin/EventHandler/Event/MethodVisibilityProviderEvent.php +++ b/src/Psalm/Plugin/EventHandler/Event/MethodVisibilityProviderEvent.php @@ -1,6 +1,5 @@ $call_args - * @param ?array $template_type_parameters + * @param ?array $template_type_parameters * @param lowercase-string $method_name_lowercase * @param lowercase-string $called_method_name_lowercase */ @@ -35,5 +36,5 @@ public static function getMethodReturnType( ?array $template_type_parameters = null, ?string $called_fq_classlike_name = null, ?string $called_method_name_lowercase = null - ): ?Type\Union; + ): ?Union; } diff --git a/src/Psalm/Plugin/Hook/MethodVisibilityProviderInterface.php b/src/Psalm/Plugin/Hook/MethodVisibilityProviderInterface.php index 9715ff6df47..bf6113521e0 100644 --- a/src/Psalm/Plugin/Hook/MethodVisibilityProviderInterface.php +++ b/src/Psalm/Plugin/Hook/MethodVisibilityProviderInterface.php @@ -1,4 +1,5 @@ config->eventDispatcher->registerClass($handler); - if (is_subclass_of($handler, Hook\PropertyExistenceProviderInterface::class) || - is_subclass_of($handler, EventHandler\PropertyExistenceProviderInterface::class) + if (is_subclass_of($handler, LegacyPropertyExistenceProviderInterface::class) || + is_subclass_of($handler, PropertyExistenceProviderInterface::class) ) { $this->codebase->properties->property_existence_provider->registerClass($handler); } - if (is_subclass_of($handler, Hook\PropertyVisibilityProviderInterface::class) || - is_subclass_of($handler, EventHandler\PropertyVisibilityProviderInterface::class) + if (is_subclass_of($handler, LegacyPropertyVisibilityProviderInterface::class) || + is_subclass_of($handler, PropertyVisibilityProviderInterface::class) ) { $this->codebase->properties->property_visibility_provider->registerClass($handler); } - if (is_subclass_of($handler, Hook\PropertyTypeProviderInterface::class) || - is_subclass_of($handler, EventHandler\PropertyTypeProviderInterface::class) + if (is_subclass_of($handler, LegacyPropertyTypeProviderInterface::class) || + is_subclass_of($handler, PropertyTypeProviderInterface::class) ) { $this->codebase->properties->property_type_provider->registerClass($handler); } - if (is_subclass_of($handler, Hook\MethodExistenceProviderInterface::class) || - is_subclass_of($handler, EventHandler\MethodExistenceProviderInterface::class) + if (is_subclass_of($handler, LegacyMethodExistenceProviderInterface::class) || + is_subclass_of($handler, MethodExistenceProviderInterface::class) ) { $this->codebase->methods->existence_provider->registerClass($handler); } - if (is_subclass_of($handler, Hook\MethodVisibilityProviderInterface::class) || - is_subclass_of($handler, EventHandler\MethodVisibilityProviderInterface::class) + if (is_subclass_of($handler, LegacyMethodVisibilityProviderInterface::class) || + is_subclass_of($handler, MethodVisibilityProviderInterface::class) ) { $this->codebase->methods->visibility_provider->registerClass($handler); } - if (is_subclass_of($handler, Hook\MethodReturnTypeProviderInterface::class) || - is_subclass_of($handler, EventHandler\MethodReturnTypeProviderInterface::class) + if (is_subclass_of($handler, LegacyMethodReturnTypeProviderInterface::class) || + is_subclass_of($handler, MethodReturnTypeProviderInterface::class) ) { $this->codebase->methods->return_type_provider->registerClass($handler); } - if (is_subclass_of($handler, Hook\MethodParamsProviderInterface::class) || - is_subclass_of($handler, EventHandler\MethodParamsProviderInterface::class) + if (is_subclass_of($handler, LegacyMethodParamsProviderInterface::class) || + is_subclass_of($handler, MethodParamsProviderInterface::class) ) { $this->codebase->methods->params_provider->registerClass($handler); } - if (is_subclass_of($handler, Hook\FunctionExistenceProviderInterface::class) || - is_subclass_of($handler, EventHandler\FunctionExistenceProviderInterface::class) + if (is_subclass_of($handler, LegacyFunctionExistenceProviderInterface::class) || + is_subclass_of($handler, FunctionExistenceProviderInterface::class) ) { $this->codebase->functions->existence_provider->registerClass($handler); } - if (is_subclass_of($handler, Hook\FunctionParamsProviderInterface::class) || - is_subclass_of($handler, EventHandler\FunctionParamsProviderInterface::class) + if (is_subclass_of($handler, LegacyFunctionParamsProviderInterface::class) || + is_subclass_of($handler, FunctionParamsProviderInterface::class) ) { $this->codebase->functions->params_provider->registerClass($handler); } - if (is_subclass_of($handler, Hook\FunctionReturnTypeProviderInterface::class) || - is_subclass_of($handler, EventHandler\FunctionReturnTypeProviderInterface::class) + if (is_subclass_of($handler, LegacyFunctionReturnTypeProviderInterface::class) || + is_subclass_of($handler, FunctionReturnTypeProviderInterface::class) ) { $this->codebase->functions->return_type_provider->registerClass($handler); } diff --git a/src/Psalm/Progress/DebugProgress.php b/src/Psalm/Progress/DebugProgress.php index fb691c3216f..192976d6693 100644 --- a/src/Psalm/Progress/DebugProgress.php +++ b/src/Psalm/Progress/DebugProgress.php @@ -1,4 +1,5 @@ >|null + * @return array>|null */ public function getTemplateTypeMap(): ?array; diff --git a/src/Psalm/Storage/Assertion.php b/src/Psalm/Storage/Assertion.php index cd77be47868..53f13b1205f 100644 --- a/src/Psalm/Storage/Assertion.php +++ b/src/Psalm/Storage/Assertion.php @@ -1,4 +1,5 @@ visibility = $visibility; $this->location = $location; diff --git a/src/Psalm/Storage/ClassLikeStorage.php b/src/Psalm/Storage/ClassLikeStorage.php index ea34a3ffb9e..4a09b9edb91 100644 --- a/src/Psalm/Storage/ClassLikeStorage.php +++ b/src/Psalm/Storage/ClassLikeStorage.php @@ -1,4 +1,5 @@ + * @var list */ public $namedMixins = []; @@ -273,12 +276,12 @@ class ClassLikeStorage public $properties = []; /** - * @var array + * @var array */ public $pseudo_property_set_types = []; /** - * @var array + * @var array */ public $pseudo_property_get_types = []; @@ -311,7 +314,7 @@ class ClassLikeStorage * (i.e. the same as the class name). This allows operations with the same-named template defined * across multiple classes to not run into trouble. * - * @var array>|null + * @var array>|null */ public $template_types; @@ -327,7 +330,7 @@ class ClassLikeStorage * * @internal * - * @var array>|null + * @var array>|null */ public $template_extended_offsets; @@ -343,7 +346,7 @@ class ClassLikeStorage * ] * ] * - * @var array>|null + * @var array>|null */ public $template_extended_params; @@ -358,7 +361,7 @@ class ClassLikeStorage public $template_type_implements_count; /** - * @var ?Type\Union + * @var ?Union */ public $yield; diff --git a/src/Psalm/Storage/CustomMetadataTrait.php b/src/Psalm/Storage/CustomMetadataTrait.php index a2d2bd00b92..85a8610a690 100644 --- a/src/Psalm/Storage/CustomMetadataTrait.php +++ b/src/Psalm/Storage/CustomMetadataTrait.php @@ -1,4 +1,5 @@ + * @var array */ public $defined_constants = []; @@ -103,7 +104,7 @@ abstract class FunctionLikeStorage public $global_variables = []; /** - * @var array + * @var array */ public $global_types = []; @@ -116,7 +117,7 @@ abstract class FunctionLikeStorage * function identifier. This allows operations with the same-named template defined * across multiple classes and/or functions to not run into trouble. * - * @var array>|null + * @var array>|null */ public $template_types; @@ -204,7 +205,7 @@ abstract class FunctionLikeStorage public $removed_taints = []; /** - * @var array + * @var array */ public $conditionally_removed_taints = []; diff --git a/src/Psalm/Storage/FunctionStorage.php b/src/Psalm/Storage/FunctionStorage.php index f7e2ff283f5..22be01b0a11 100644 --- a/src/Psalm/Storage/FunctionStorage.php +++ b/src/Psalm/Storage/FunctionStorage.php @@ -1,4 +1,5 @@ > $template_type_map + * @param array> $template_type_map */ public static function parseString( string $type_string, @@ -194,7 +198,7 @@ public static function getLowercaseString(): Union public static function getPositiveInt(bool $from_calculation = false): Union { - $union = new Union([new Type\Atomic\TPositiveInt()]); + $union = new Union([new TPositiveInt()]); $union->from_calculation = $from_calculation; return $union; @@ -243,7 +247,7 @@ public static function getString(?string $value = null): Union if (strlen($value) < $config->max_string_length) { $type = new TLiteralString($value); } else { - $type = new Type\Atomic\TNonEmptyString(); + $type = new TNonEmptyString(); } } } @@ -346,7 +350,7 @@ public static function getObject(): Union public static function getClosure(): Union { - $type = new Type\Atomic\TClosure('Closure'); + $type = new TClosure('Closure'); return new Union([$type]); } @@ -362,8 +366,8 @@ public static function getArray(): Union { $type = new TArray( [ - new Type\Union([new TArrayKey]), - new Type\Union([new TMixed]), + new Union([new TArrayKey]), + new Union([new TMixed]), ] ); @@ -374,26 +378,26 @@ public static function getEmptyArray(): Union { $array_type = new TArray( [ - new Type\Union([new TEmpty]), - new Type\Union([new TEmpty]), + new Union([new TEmpty]), + new Union([new TEmpty]), ] ); - return new Type\Union([ + return new Union([ $array_type, ]); } public static function getList(): Union { - $type = new TList(new Type\Union([new TMixed])); + $type = new TList(new Union([new TMixed])); return new Union([$type]); } public static function getNonEmptyList(): Union { - $type = new Type\Atomic\TNonEmptyList(new Type\Union([new TMixed])); + $type = new TNonEmptyList(new Union([new TMixed])); return new Union([$type]); } @@ -425,9 +429,9 @@ public static function getResource(): Union } /** - * @param non-empty-list $union_types + * @param non-empty-list $union_types */ - public static function combineUnionTypeArray(array $union_types, ?Codebase $codebase): Type\Union + public static function combineUnionTypeArray(array $union_types, ?Codebase $codebase): Union { $first_type = array_pop($union_types); diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 7e39f434a58..b806ac200c6 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -1,4 +1,5 @@ 'string']); + return new TObjectWithProperties([], ['__tostring' => 'string']); case 'class-string': case 'interface-string': @@ -265,13 +280,13 @@ public static function create( return new THtmlEscapedString(); case 'literal-string': - return new Type\Atomic\TNonspecificLiteralString(); + return new TNonspecificLiteralString(); case 'non-empty-literal-string': - return new Type\Atomic\TNonEmptyNonspecificLiteralString(); + return new TNonEmptyNonspecificLiteralString(); case 'literal-int': - return new Type\Atomic\TNonspecificLiteralInt(); + return new TNonspecificLiteralInt(); case 'false-y': return new TAssertionFalsy(); @@ -310,7 +325,7 @@ public static function create( if (isset($type_aliases[$value])) { $type_alias = $type_aliases[$value]; - if ($type_alias instanceof TypeAlias\LinkableTypeAlias) { + if ($type_alias instanceof LinkableTypeAlias) { return new TTypeAlias($type_alias->declaring_fq_classlike_name, $type_alias->alias_name); } @@ -361,7 +376,8 @@ public function isCallableType(): bool || $this instanceof TCallableString || $this instanceof TCallableArray || $this instanceof TCallableList - || $this instanceof TCallableKeyedArray; + || $this instanceof TCallableKeyedArray + || $this instanceof TClosure; } public function isIterable(Codebase $codebase): bool @@ -436,7 +452,7 @@ public function isArrayAccessibleWithStringKey(Codebase $codebase): bool return $this instanceof TArray || $this instanceof TKeyedArray || $this instanceof TList - || $this instanceof Atomic\TClassStringMap + || $this instanceof TClassStringMap || $this->hasArrayAccessInterface($codebase) || ($this instanceof TNamedObject && $this->value === 'SimpleXMLElement'); } @@ -518,23 +534,23 @@ public function replaceClassLike(string $old, string $new): void } } - if ($this instanceof Type\Atomic\TArray - || $this instanceof Type\Atomic\TGenericObject - || $this instanceof Type\Atomic\TIterable + if ($this instanceof TArray + || $this instanceof TGenericObject + || $this instanceof TIterable ) { foreach ($this->type_params as $type_param) { $type_param->replaceClassLike($old, $new); } } - if ($this instanceof Type\Atomic\TKeyedArray) { + if ($this instanceof TKeyedArray) { foreach ($this->properties as $property_type) { $property_type->replaceClassLike($old, $new); } } - if ($this instanceof Type\Atomic\TClosure - || $this instanceof Type\Atomic\TCallable + if ($this instanceof TClosure + || $this instanceof TCallable ) { if ($this->params) { foreach ($this->params as $param) { @@ -560,7 +576,7 @@ public function __clone() if ($this instanceof TNamedObject || $this instanceof TTemplateParam || $this instanceof TIterable - || $this instanceof Type\Atomic\TObjectWithProperties + || $this instanceof TObjectWithProperties ) { if ($this->extra_types) { foreach ($this->extra_types as &$type) { @@ -613,7 +629,7 @@ public function replaceTemplateTypesWithStandins( TemplateResult $template_result, ?Codebase $codebase = null, ?StatementsAnalyzer $statements_analyzer = null, - Type\Atomic $input_type = null, + Atomic $input_type = null, ?int $input_arg_offset = null, ?string $calling_class = null, ?string $calling_function = null, diff --git a/src/Psalm/Type/Atomic/CallableTrait.php b/src/Psalm/Type/Atomic/CallableTrait.php index 9b1224ce6c4..cd0c026d0c1 100644 --- a/src/Psalm/Type/Atomic/CallableTrait.php +++ b/src/Psalm/Type/Atomic/CallableTrait.php @@ -1,4 +1,5 @@ params as $offset => $param) { $input_param_type = null; - if (($input_type instanceof Atomic\TClosure || $input_type instanceof Atomic\TCallable) + if (($input_type instanceof TClosure || $input_type instanceof TCallable) && isset($input_type->params[$offset]) ) { $input_param_type = $input_type->params[$offset]->type; @@ -231,7 +235,7 @@ public function replaceTemplateTypesWithStandins( $template_result, $codebase, $statements_analyzer, - $input_type instanceof Atomic\TCallable || $input_type instanceof Atomic\TClosure + $input_type instanceof TCallable || $input_type instanceof TClosure ? $input_type->return_type : null, $input_arg_offset, diff --git a/src/Psalm/Type/Atomic/DependentType.php b/src/Psalm/Type/Atomic/DependentType.php index 0d5dada6bdd..4ebefe03b38 100644 --- a/src/Psalm/Type/Atomic/DependentType.php +++ b/src/Psalm/Type/Atomic/DependentType.php @@ -1,4 +1,5 @@ type_param]); + if ($input_type instanceof TList) { + $input_type = new TArray([Type::getInt(), $input_type->type_param]); } $input_object_type_params = []; $container_type_params_covariant = []; - if ($input_type instanceof Atomic\TGenericObject - && ($this instanceof Atomic\TGenericObject || $this instanceof Atomic\TIterable) + if ($input_type instanceof TGenericObject + && ($this instanceof TGenericObject || $this instanceof TIterable) && $codebase ) { $input_object_type_params = TemplateStandinTypeReplacer::getMappedGenericTypeParams( @@ -217,13 +224,13 @@ public function replaceTemplateTypesWithStandins( foreach ($atomic->type_params as $offset => $type_param) { $input_type_param = null; - if (($input_type instanceof Atomic\TIterable - || $input_type instanceof Atomic\TArray) + if (($input_type instanceof TIterable + || $input_type instanceof TArray) && isset($input_type->type_params[$offset]) ) { $input_type_param = $input_type->type_params[$offset]; - } elseif ($input_type instanceof Atomic\TKeyedArray) { + } elseif ($input_type instanceof TKeyedArray) { if ($offset === 0) { $input_type_param = $input_type->getGenericKeyType(); } elseif ($offset === 1) { @@ -231,7 +238,7 @@ public function replaceTemplateTypesWithStandins( } else { throw new UnexpectedValueException('Not expecting offset of ' . $offset); } - } elseif ($input_type instanceof Atomic\TNamedObject + } elseif ($input_type instanceof TNamedObject && isset($input_object_type_params[$offset]) ) { $input_type_param = $input_object_type_params[$offset]; @@ -250,7 +257,7 @@ public function replaceTemplateTypesWithStandins( $replace, $add_lower_bound, !($container_type_params_covariant[$offset] ?? true) - && $this instanceof Atomic\TGenericObject + && $this instanceof TGenericObject ? $this->value : null, $depth + 1 @@ -271,7 +278,7 @@ public function replaceTemplateTypesWithArgTypes( $codebase ); - if ($this instanceof Atomic\TArray && $offset === 0 && $type_param->isMixed()) { + if ($this instanceof TArray && $offset === 0 && $type_param->isMixed()) { $this->type_params[0] = Type::getArrayKey(); } } diff --git a/src/Psalm/Type/Atomic/HasIntersectionTrait.php b/src/Psalm/Type/Atomic/HasIntersectionTrait.php index 5865647c799..41743192c91 100644 --- a/src/Psalm/Type/Atomic/HasIntersectionTrait.php +++ b/src/Psalm/Type/Atomic/HasIntersectionTrait.php @@ -1,10 +1,10 @@ extra_types[$type->getKey()] = $type; } diff --git a/src/Psalm/Type/Atomic/Scalar.php b/src/Psalm/Type/Atomic/Scalar.php index ed208d9d187..f37518cd289 100644 --- a/src/Psalm/Type/Atomic/Scalar.php +++ b/src/Psalm/Type/Atomic/Scalar.php @@ -1,4 +1,5 @@ value_param] as $offset => $type_param) { $input_type_param = null; - if (($input_type instanceof Atomic\TGenericObject - || $input_type instanceof Atomic\TIterable - || $input_type instanceof Atomic\TArray) + if (($input_type instanceof TGenericObject + || $input_type instanceof TIterable + || $input_type instanceof TArray) && isset($input_type->type_params[$offset]) ) { $input_type_param = clone $input_type->type_params[$offset]; - } elseif ($input_type instanceof Atomic\TKeyedArray) { + } elseif ($input_type instanceof TKeyedArray) { if ($offset === 0) { $input_type_param = $input_type->getGenericKeyType(); } else { $input_type_param = $input_type->getGenericValueType(); } - } elseif ($input_type instanceof Atomic\TList) { + } elseif ($input_type instanceof TList) { if ($offset === 0) { continue; } @@ -229,9 +237,9 @@ public function getAssertionString(bool $exact = false): string return $this->getKey(); } - public function getStandinKeyParam(): Type\Union + public function getStandinKeyParam(): Union { - return new Type\Union([ + return new Union([ new TTemplateParamClass( $this->param_name, $this->as_type->value ?? 'object', diff --git a/src/Psalm/Type/Atomic/TClosedResource.php b/src/Psalm/Type/Atomic/TClosedResource.php index 18cc06f6e82..c8391b4c532 100644 --- a/src/Psalm/Type/Atomic/TClosedResource.php +++ b/src/Psalm/Type/Atomic/TClosedResource.php @@ -1,4 +1,5 @@ class_strings[$name])) { + $class_string_suffix = '::class'; } - return $name . ($type->possibly_undefined ? '?' : '') . ': ' . $type; + $name = $this->escapeAndQuote($name); + + return $name . $class_string_suffix . ($type->possibly_undefined ? '?' : '') . ': ' . $type; }, array_keys($this->properties), $this->properties @@ -111,11 +122,14 @@ function ($name, Union $type): string { return $type->getId(); } - if (is_string($name) && preg_match('/[ "\'\\\\.\n:]/', $name)) { - $name = '\'' . str_replace("\n", '\n', addslashes($name)) . '\''; + $class_string_suffix = ''; + if (isset($this->class_strings[$name])) { + $class_string_suffix = '::class'; } - return $name . ($type->possibly_undefined ? '?' : '') . ': ' . $type->getId(); + $name = $this->escapeAndQuote($name); + + return $name . $class_string_suffix . ($type->possibly_undefined ? '?' : '') . ': ' . $type->getId(); }, array_keys($this->properties), $this->properties @@ -170,16 +184,20 @@ function ( $this_class, $use_phpdoc_format ): string { - if (is_string($name) && preg_match('/[ "\'\\\\.\n:]/', $name)) { - $name = '\'' . str_replace("\n", '\n', addslashes($name)) . '\''; + $class_string_suffix = ''; + if (isset($this->class_strings[$name])) { + $class_string_suffix = '::class'; } - return $name . ($type->possibly_undefined ? '?' : '') . ': ' . $type->toNamespacedString( - $namespace, - $aliased_classes, - $this_class, - $use_phpdoc_format - ); + $name = $this->escapeAndQuote($name); + + return $name . $class_string_suffix . ($type->possibly_undefined ? '?' : '') . ': ' . + $type->toNamespacedString( + $namespace, + $aliased_classes, + $this_class, + $use_phpdoc_format + ); }, array_keys($this->properties), $this->properties @@ -212,11 +230,11 @@ public function getGenericKeyType(): Union foreach ($this->properties as $key => $_) { if (is_int($key)) { - $key_types[] = new Type\Atomic\TLiteralInt($key); + $key_types[] = new TLiteralInt($key); } elseif (isset($this->class_strings[$key])) { - $key_types[] = new Type\Atomic\TLiteralClassString($key); + $key_types[] = new TLiteralClassString($key); } else { - $key_types[] = new Type\Atomic\TLiteralString($key); + $key_types[] = new TLiteralString($key); } } @@ -251,11 +269,11 @@ public function getGenericArrayType(bool $allow_non_empty = true): TArray foreach ($this->properties as $key => $property) { if (is_int($key)) { - $key_types[] = new Type\Atomic\TLiteralInt($key); + $key_types[] = new TLiteralInt($key); } elseif (isset($this->class_strings[$key])) { - $key_types[] = new Type\Atomic\TLiteralClassString($key); + $key_types[] = new TLiteralClassString($key); } else { - $key_types[] = new Type\Atomic\TLiteralString($key); + $key_types[] = new TLiteralString($key); } $value_type = Type::combineUnionTypes(clone $property, $value_type); @@ -322,7 +340,7 @@ public function replaceTemplateTypesWithStandins( foreach ($this->properties as $offset => $property) { $input_type_param = null; - if ($input_type instanceof Atomic\TKeyedArray + if ($input_type instanceof TKeyedArray && isset($input_type->properties[$offset]) ) { $input_type_param = $input_type->properties[$offset]; @@ -405,4 +423,17 @@ public function getList(): TNonEmptyList return new TNonEmptyList($this->getGenericValueType()); } + + /** + * @param string|int $name + * @return string|int + */ + private function escapeAndQuote($name) + { + if (is_string($name) && preg_match('/[ "\'\\\\.\n:]/', $name)) { + $name = '\'' . str_replace("\n", '\n', addslashes($name)) . '\''; + } + + return $name; + } } diff --git a/src/Psalm/Type/Atomic/TList.php b/src/Psalm/Type/Atomic/TList.php index f6e240056e9..3d5136530d4 100644 --- a/src/Psalm/Type/Atomic/TList.php +++ b/src/Psalm/Type/Atomic/TList.php @@ -1,4 +1,5 @@ type_param] as $offset => $type_param) { $input_type_param = null; - if (($input_type instanceof Atomic\TGenericObject - || $input_type instanceof Atomic\TIterable - || $input_type instanceof Atomic\TArray) + if (($input_type instanceof TGenericObject + || $input_type instanceof TIterable + || $input_type instanceof TArray) && isset($input_type->type_params[$offset]) ) { $input_type_param = clone $input_type->type_params[$offset]; - } elseif ($input_type instanceof Atomic\TKeyedArray) { + } elseif ($input_type instanceof TKeyedArray) { if ($offset === 0) { $input_type_param = $input_type->getGenericKeyType(); } else { $input_type_param = $input_type->getGenericValueType(); } - } elseif ($input_type instanceof Atomic\TList) { + } elseif ($input_type instanceof TList) { if ($offset === 0) { continue; } diff --git a/src/Psalm/Type/Atomic/TLiteralClassString.php b/src/Psalm/Type/Atomic/TLiteralClassString.php index 564d6c7cd00..a36429d3cf7 100644 --- a/src/Psalm/Type/Atomic/TLiteralClassString.php +++ b/src/Psalm/Type/Atomic/TLiteralClassString.php @@ -1,4 +1,5 @@ properties as $offset => $property) { $input_type_param = null; - if ($input_type instanceof Atomic\TKeyedArray + if ($input_type instanceof TKeyedArray && isset($input_type->properties[$offset]) ) { $input_type_param = $input_type->properties[$offset]; diff --git a/src/Psalm/Type/Atomic/TPositiveInt.php b/src/Psalm/Type/Atomic/TPositiveInt.php index 72711c5295a..eb345a0ba1a 100644 --- a/src/Psalm/Type/Atomic/TPositiveInt.php +++ b/src/Psalm/Type/Atomic/TPositiveInt.php @@ -1,4 +1,5 @@ >> $new_types * @param array>> $active_new_types - types we can complain about - * @param array $existing_types + * @param array $existing_types * @param array $changed_var_ids * @param array $referenced_var_ids - * @param array> $template_type_map + * @param array> $template_type_map * - * @return array + * @return array */ public static function reconcileKeyedTypes( array $new_types, @@ -322,7 +330,7 @@ public static function reconcileKeyedTypes( * ] * * @param array>> $new_types - * @param array $existing_types + * @param array $existing_types * * @return array>> */ @@ -536,7 +544,7 @@ public static function breakUpPathIntoParts(string $path): array /** * Gets the type for a given (non-existent key) based on the passed keys * - * @param array $existing_keys + * @param array $existing_keys * @param array $new_assertions */ private static function getValueForKey( @@ -617,7 +625,7 @@ private static function getValueForKey( continue; } - if ($existing_key_type_part instanceof Type\Atomic\TArray) { + if ($existing_key_type_part instanceof TArray) { if ($has_empty) { return null; } @@ -630,12 +638,12 @@ private static function getValueForKey( if (($has_isset || $has_inverted_isset) && isset($new_assertions[$new_base_key])) { if ($has_inverted_isset && $new_base_key === $key) { - $new_base_type_candidate->addType(new Type\Atomic\TNull); + $new_base_type_candidate->addType(new TNull); } $new_base_type_candidate->possibly_undefined = true; } - } elseif ($existing_key_type_part instanceof Type\Atomic\TList) { + } elseif ($existing_key_type_part instanceof TList) { if ($has_empty) { return null; } @@ -644,29 +652,29 @@ private static function getValueForKey( if (($has_isset || $has_inverted_isset) && isset($new_assertions[$new_base_key])) { if ($has_inverted_isset && $new_base_key === $key) { - $new_base_type_candidate->addType(new Type\Atomic\TNull); + $new_base_type_candidate->addType(new TNull); } $new_base_type_candidate->possibly_undefined = true; } - } elseif ($existing_key_type_part instanceof Type\Atomic\TNull - || $existing_key_type_part instanceof Type\Atomic\TFalse + } elseif ($existing_key_type_part instanceof TNull + || $existing_key_type_part instanceof TFalse ) { $new_base_type_candidate = Type::getNull(); if ($existing_keys[$base_key]->ignore_nullable_issues) { $new_base_type_candidate->ignore_nullable_issues = true; } - } elseif ($existing_key_type_part instanceof Type\Atomic\TClassStringMap) { + } elseif ($existing_key_type_part instanceof TClassStringMap) { return Type::getMixed(); - } elseif ($existing_key_type_part instanceof Type\Atomic\TEmpty - || ($existing_key_type_part instanceof Type\Atomic\TMixed + } elseif ($existing_key_type_part instanceof TEmpty + || ($existing_key_type_part instanceof TMixed && $existing_key_type_part->from_loop_isset) ) { return Type::getMixed($inside_loop); } elseif ($existing_key_type_part instanceof TString) { $new_base_type_candidate = Type::getString(); - } elseif ($existing_key_type_part instanceof Type\Atomic\TNamedObject + } elseif ($existing_key_type_part instanceof TNamedObject && ($has_isset || $has_inverted_isset) ) { $has_object_array_access = true; @@ -674,7 +682,7 @@ private static function getValueForKey( unset($existing_keys[$new_base_key]); return null; - } elseif (!$existing_key_type_part instanceof Type\Atomic\TKeyedArray) { + } elseif (!$existing_key_type_part instanceof TKeyedArray) { return Type::getMixed(); } elseif ($array_key[0] === '$' || ($array_key[0] !== '\'' && !is_numeric($array_key[0]))) { if ($has_empty) { @@ -830,7 +838,7 @@ private static function getPropertyType( Codebase $codebase, string $fq_class_name, string $property_name - ): ?Type\Union { + ): ?Union { $property_id = $fq_class_name . '::$' . $property_name; if (!$codebase->properties->propertyExists($property_id, true)) { @@ -995,14 +1003,14 @@ protected static function triggerIssueForImpossible( /** * @param string[] $key_parts - * @param array $existing_types + * @param array $existing_types * @param array $changed_var_ids */ private static function adjustTKeyedArrayType( array $key_parts, array &$existing_types, array &$changed_var_ids, - Type\Union $result_type + Union $result_type ): void { array_pop($key_parts); $array_key = array_pop($key_parts); @@ -1022,19 +1030,19 @@ private static function adjustTKeyedArrayType( if (isset($existing_types[$base_key]) && $array_key_offset !== false) { foreach ($existing_types[$base_key]->getAtomicTypes() as $base_atomic_type) { - if ($base_atomic_type instanceof Type\Atomic\TKeyedArray - || ($base_atomic_type instanceof Type\Atomic\TArray + if ($base_atomic_type instanceof TKeyedArray + || ($base_atomic_type instanceof TArray && !$base_atomic_type->type_params[1]->isEmpty()) - || $base_atomic_type instanceof Type\Atomic\TList - || $base_atomic_type instanceof Type\Atomic\TClassStringMap + || $base_atomic_type instanceof TList + || $base_atomic_type instanceof TClassStringMap ) { $new_base_type = clone $existing_types[$base_key]; - if ($base_atomic_type instanceof Type\Atomic\TArray) { + if ($base_atomic_type instanceof TArray) { $previous_key_type = clone $base_atomic_type->type_params[0]; $previous_value_type = clone $base_atomic_type->type_params[1]; - $base_atomic_type = new Type\Atomic\TKeyedArray( + $base_atomic_type = new TKeyedArray( [ $array_key_offset => clone $result_type, ], @@ -1045,11 +1053,11 @@ private static function adjustTKeyedArrayType( $base_atomic_type->previous_key_type = $previous_key_type; } $base_atomic_type->previous_value_type = $previous_value_type; - } elseif ($base_atomic_type instanceof Type\Atomic\TList) { + } elseif ($base_atomic_type instanceof TList) { $previous_key_type = Type::getInt(); $previous_value_type = clone $base_atomic_type->type_param; - $base_atomic_type = new Type\Atomic\TKeyedArray( + $base_atomic_type = new TKeyedArray( [ $array_key_offset => clone $result_type, ], @@ -1060,7 +1068,7 @@ private static function adjustTKeyedArrayType( $base_atomic_type->previous_key_type = $previous_key_type; $base_atomic_type->previous_value_type = $previous_value_type; - } elseif ($base_atomic_type instanceof Type\Atomic\TClassStringMap) { + } elseif ($base_atomic_type instanceof TClassStringMap) { // do nothing } else { $base_atomic_type = clone $base_atomic_type; @@ -1095,17 +1103,17 @@ protected static function refineArrayKey(Union $key_type): void $key_type->bustCache(); } elseif ($cat instanceof TScalar || $cat instanceof TMixed) { $key_type->removeType($key); - $key_type->addType(new Type\Atomic\TArrayKey()); + $key_type->addType(new TArrayKey()); } elseif (!$cat instanceof TString && !$cat instanceof TInt) { $key_type->removeType($key); - $key_type->addType(new Type\Atomic\TArrayKey()); + $key_type->addType(new TArrayKey()); } } /** @psalm-suppress TypeDoesNotContainType can be empty after removing above */ if (!$key_type->getAtomicTypes()) { // this should ideally prompt some sort of error - $key_type->addType(new Type\Atomic\TArrayKey()); + $key_type->addType(new TArrayKey()); } } } diff --git a/src/Psalm/Type/TypeNode.php b/src/Psalm/Type/TypeNode.php index 24de98f7538..6bdc054c28c 100644 --- a/src/Psalm/Type/TypeNode.php +++ b/src/Psalm/Type/TypeNode.php @@ -1,4 +1,5 @@ + * @var array */ private $typed_class_strings = []; @@ -228,8 +262,8 @@ public function __construct(array $types) $this->literal_string_types[$key] = $type; } elseif ($type instanceof TLiteralFloat) { $this->literal_float_types[$key] = $type; - } elseif ($type instanceof Type\Atomic\TClassString - && ($type->as_type || $type instanceof Type\Atomic\TTemplateParamClass) + } elseif ($type instanceof TClassString + && ($type->as_type || $type instanceof TTemplateParamClass) ) { $this->typed_class_strings[$key] = $type; } @@ -272,8 +306,8 @@ public function addType(Atomic $type): void foreach ($this->literal_string_types as $key => $_) { unset($this->literal_string_types[$key], $this->types[$key]); } - if (!$type instanceof Type\Atomic\TClassString - || (!$type->as_type && !$type instanceof Type\Atomic\TTemplateParamClass) + if (!$type instanceof TClassString + || (!$type->as_type && !$type instanceof TTemplateParamClass) ) { foreach ($this->typed_class_strings as $key => $_) { unset($this->typed_class_strings[$key], $this->types[$key]); @@ -312,8 +346,8 @@ public function __clone() $this->literal_string_types[$key] = $type; } elseif ($type instanceof TLiteralFloat) { $this->literal_float_types[$key] = $type; - } elseif ($type instanceof Type\Atomic\TClassString - && ($type->as_type || $type instanceof Type\Atomic\TTemplateParamClass) + } elseif ($type instanceof TClassString + && ($type->as_type || $type instanceof TTemplateParamClass) ) { $this->typed_class_strings[$key] = $type; } @@ -656,12 +690,12 @@ public function hasIterable(): bool public function hasList(): bool { - return isset($this->types['array']) && $this->types['array'] instanceof Atomic\TList; + return isset($this->types['array']) && $this->types['array'] instanceof TList; } public function hasClassStringMap(): bool { - return isset($this->types['array']) && $this->types['array'] instanceof Atomic\TClassStringMap; + return isset($this->types['array']) && $this->types['array'] instanceof TClassStringMap; } public function isTemplatedClassString(): bool @@ -671,7 +705,7 @@ public function isTemplatedClassString(): bool array_filter( $this->types, function ($type): bool { - return $type instanceof Atomic\TTemplateParamClass; + return $type instanceof TTemplateParamClass; } ) ) === 1; @@ -693,27 +727,27 @@ public function hasCallableType(): bool } /** - * @return array + * @return array */ public function getCallableTypes(): array { return array_filter( $this->types, function ($type): bool { - return $type instanceof Atomic\TCallable; + return $type instanceof TCallable; } ); } /** - * @return array + * @return array */ public function getClosureTypes(): array { return array_filter( $this->types, function ($type): bool { - return $type instanceof Atomic\TClosure; + return $type instanceof TClosure; } ); } @@ -832,8 +866,8 @@ public function hasString(): bool public function hasLowercaseString(): bool { return isset($this->types['string']) - && ($this->types['string'] instanceof Atomic\TLowercaseString - || $this->types['string'] instanceof Atomic\TNonEmptyLowercaseString); + && ($this->types['string'] instanceof TLowercaseString + || $this->types['string'] instanceof TNonEmptyLowercaseString); } public function hasLiteralClassString(): bool @@ -845,13 +879,13 @@ public function hasInt(): bool { return isset($this->types['int']) || isset($this->types['array-key']) || $this->literal_int_types || array_filter($this->types, function (Atomic $type) { - return $type instanceof Type\Atomic\TIntRange; + return $type instanceof TIntRange; }); } public function hasPositiveInt(): bool { - return isset($this->types['int']) && $this->types['int'] instanceof Type\Atomic\TPositiveInt; + return isset($this->types['int']) && $this->types['int'] instanceof TPositiveInt; } public function hasArrayKey(): bool @@ -897,13 +931,13 @@ public function hasTemplate(): bool return (bool) array_filter( $this->types, function (Atomic $type): bool { - return $type instanceof Type\Atomic\TTemplateParam - || ($type instanceof Type\Atomic\TNamedObject + return $type instanceof TTemplateParam + || ($type instanceof TNamedObject && $type->extra_types && array_filter( $type->extra_types, function ($t): bool { - return $t instanceof Type\Atomic\TTemplateParam; + return $t instanceof TTemplateParam; } ) ); @@ -916,7 +950,7 @@ public function hasConditional(): bool return (bool) array_filter( $this->types, function (Atomic $type): bool { - return $type instanceof Type\Atomic\TConditional; + return $type instanceof TConditional; } ); } @@ -926,14 +960,14 @@ public function hasTemplateOrStatic(): bool return (bool) array_filter( $this->types, function (Atomic $type): bool { - return $type instanceof Type\Atomic\TTemplateParam - || ($type instanceof Type\Atomic\TNamedObject + return $type instanceof TTemplateParam + || ($type instanceof TNamedObject && ($type->was_static || ($type->extra_types && array_filter( $type->extra_types, function ($t): bool { - return $t instanceof Type\Atomic\TTemplateParam; + return $t instanceof TTemplateParam; } ) ) @@ -956,14 +990,14 @@ public function isMixed(): bool public function isEmptyMixed(): bool { return isset($this->types['mixed']) - && $this->types['mixed'] instanceof Type\Atomic\TEmptyMixed + && $this->types['mixed'] instanceof TEmptyMixed && count($this->types) === 1; } public function isVanillaMixed(): bool { return isset($this->types['mixed']) - && get_class($this->types['mixed']) === Type\Atomic\TMixed::class + && get_class($this->types['mixed']) === TMixed::class && !$this->types['mixed']->from_loop_isset && count($this->types) === 1; } @@ -986,37 +1020,37 @@ public function isFalse(): bool public function isAlwaysFalsy(): bool { foreach ($this->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TFalse) { + if ($atomic_type instanceof TFalse) { continue; } - if ($atomic_type instanceof Type\Atomic\TLiteralInt && $atomic_type->value === 0) { + if ($atomic_type instanceof TLiteralInt && $atomic_type->value === 0) { continue; } - if ($atomic_type instanceof Type\Atomic\TLiteralFloat && $atomic_type->value === 0.0) { + if ($atomic_type instanceof TLiteralFloat && $atomic_type->value === 0.0) { continue; } - if ($atomic_type instanceof Type\Atomic\TLiteralString && + if ($atomic_type instanceof TLiteralString && ($atomic_type->value === '' || $atomic_type->value === '0') ) { continue; } - if ($atomic_type instanceof Type\Atomic\TNull) { + if ($atomic_type instanceof TNull) { continue; } - if ($atomic_type instanceof Type\Atomic\TEmptyMixed) { + if ($atomic_type instanceof TEmptyMixed) { continue; } - if ($atomic_type instanceof Type\Atomic\TEmptyNumeric) { + if ($atomic_type instanceof TEmptyNumeric) { continue; } - if ($atomic_type instanceof Type\Atomic\TEmptyScalar) { + if ($atomic_type instanceof TEmptyScalar) { continue; } @@ -1024,14 +1058,14 @@ public function isAlwaysFalsy(): bool continue; } - if ($atomic_type instanceof Type\Atomic\TIntRange && + if ($atomic_type instanceof TIntRange && $atomic_type->min_bound === 0 && $atomic_type->max_bound === 0 ) { continue; } - if ($atomic_type instanceof Type\Atomic\TArray && $atomic_type->getId() === 'array') { + if ($atomic_type instanceof TArray && $atomic_type->getId() === 'array') { continue; } @@ -1054,87 +1088,87 @@ public function isAlwaysTruthy(): bool } foreach ($this->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof Type\Atomic\TTrue) { + if ($atomic_type instanceof TTrue) { continue; } - if ($atomic_type instanceof Type\Atomic\TLiteralInt && $atomic_type->value !== 0) { + if ($atomic_type instanceof TLiteralInt && $atomic_type->value !== 0) { continue; } - if ($atomic_type instanceof Type\Atomic\TLiteralFloat && $atomic_type->value !== 0.0) { + if ($atomic_type instanceof TLiteralFloat && $atomic_type->value !== 0.0) { continue; } - if ($atomic_type instanceof Type\Atomic\TLiteralString && + if ($atomic_type instanceof TLiteralString && ($atomic_type->value !== '' && $atomic_type->value !== '0') ) { continue; } - if ($atomic_type instanceof Type\Atomic\TNonFalsyString) { + if ($atomic_type instanceof TNonFalsyString) { continue; } - if ($atomic_type instanceof Type\Atomic\TCallableString) { + if ($atomic_type instanceof TCallableString) { continue; } - if ($atomic_type instanceof Type\Atomic\TNonEmptyArray) { + if ($atomic_type instanceof TNonEmptyArray) { continue; } - if ($atomic_type instanceof Type\Atomic\TNonEmptyScalar) { + if ($atomic_type instanceof TNonEmptyScalar) { continue; } - if ($atomic_type instanceof Type\Atomic\TNonEmptyList) { + if ($atomic_type instanceof TNonEmptyList) { continue; } - if ($atomic_type instanceof Type\Atomic\TNonEmptyMixed) { + if ($atomic_type instanceof TNonEmptyMixed) { continue; } - if ($atomic_type instanceof Type\Atomic\TObject) { + if ($atomic_type instanceof TObject) { continue; } - if ($atomic_type instanceof Type\Atomic\TNamedObject + if ($atomic_type instanceof TNamedObject && $atomic_type->value !== 'SimpleXMLElement' && $atomic_type->value !== 'SimpleXMLIterator') { continue; } - if ($atomic_type instanceof Type\Atomic\TIntRange && !$atomic_type->contains(0)) { + if ($atomic_type instanceof TIntRange && !$atomic_type->contains(0)) { continue; } - if ($atomic_type instanceof Type\Atomic\TPositiveInt) { + if ($atomic_type instanceof TPositiveInt) { continue; } - if ($atomic_type instanceof Type\Atomic\TLiteralClassString) { + if ($atomic_type instanceof TLiteralClassString) { continue; } - if ($atomic_type instanceof Type\Atomic\TClassString) { + if ($atomic_type instanceof TClassString) { continue; } - if ($atomic_type instanceof Type\Atomic\TDependentGetClass) { + if ($atomic_type instanceof TDependentGetClass) { continue; } - if ($atomic_type instanceof Type\Atomic\TTraitString) { + if ($atomic_type instanceof TTraitString) { continue; } - if ($atomic_type instanceof Type\Atomic\TResource) { + if ($atomic_type instanceof TResource) { continue; } - if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + if ($atomic_type instanceof TKeyedArray) { foreach ($atomic_type->properties as $property) { if ($property->possibly_undefined === false) { continue 2; @@ -1191,46 +1225,46 @@ public function substitute(Union $old_type, ?Union $new_type = null): void foreach ($old_type->types as $old_type_part) { if (!$this->removeType($old_type_part->getKey())) { - if ($old_type_part instanceof Type\Atomic\TFalse + if ($old_type_part instanceof TFalse && isset($this->types['bool']) && !isset($this->types['true']) ) { $this->removeType('bool'); - $this->types['true'] = new Type\Atomic\TTrue; - } elseif ($old_type_part instanceof Type\Atomic\TTrue + $this->types['true'] = new TTrue; + } elseif ($old_type_part instanceof TTrue && isset($this->types['bool']) && !isset($this->types['false']) ) { $this->removeType('bool'); - $this->types['false'] = new Type\Atomic\TFalse; + $this->types['false'] = new TFalse; } elseif (isset($this->types['iterable'])) { - if ($old_type_part instanceof Type\Atomic\TNamedObject + if ($old_type_part instanceof TNamedObject && $old_type_part->value === 'Traversable' && !isset($this->types['array']) ) { $this->removeType('iterable'); - $this->types['array'] = new Type\Atomic\TArray([Type::getArrayKey(), Type::getMixed()]); + $this->types['array'] = new TArray([Type::getArrayKey(), Type::getMixed()]); } - if ($old_type_part instanceof Type\Atomic\TArray + if ($old_type_part instanceof TArray && !isset($this->types['traversable']) ) { $this->removeType('iterable'); - $this->types['traversable'] = new Type\Atomic\TNamedObject('Traversable'); + $this->types['traversable'] = new TNamedObject('Traversable'); } } elseif (isset($this->types['array-key'])) { - if ($old_type_part instanceof Type\Atomic\TString + if ($old_type_part instanceof TString && !isset($this->types['int']) ) { $this->removeType('array-key'); - $this->types['int'] = new Type\Atomic\TInt(); + $this->types['int'] = new TInt(); } - if ($old_type_part instanceof Type\Atomic\TInt + if ($old_type_part instanceof TInt && !isset($this->types['string']) ) { $this->removeType('array-key'); - $this->types['string'] = new Type\Atomic\TString(); + $this->types['string'] = new TString(); } } } @@ -1239,7 +1273,7 @@ public function substitute(Union $old_type, ?Union $new_type = null): void if ($new_type) { foreach ($new_type->types as $key => $new_type_part) { if (!isset($this->types[$key]) - || ($new_type_part instanceof Type\Atomic\Scalar + || ($new_type_part instanceof Scalar && get_class($new_type_part) === get_class($this->types[$key])) ) { $this->types[$key] = $new_type_part; @@ -1248,7 +1282,7 @@ public function substitute(Union $old_type, ?Union $new_type = null): void } } } elseif (count($this->types) === 0) { - $this->types['mixed'] = new Atomic\TMixed(); + $this->types['mixed'] = new TMixed(); } $this->id = null; @@ -1428,10 +1462,10 @@ public function allLiterals(): bool if (!$atomic_key_type instanceof TLiteralString && !$atomic_key_type instanceof TLiteralInt && !$atomic_key_type instanceof TLiteralFloat - && !$atomic_key_type instanceof Atomic\TNonspecificLiteralString - && !$atomic_key_type instanceof Atomic\TNonspecificLiteralInt - && !$atomic_key_type instanceof Atomic\TFalse - && !$atomic_key_type instanceof Atomic\TTrue + && !$atomic_key_type instanceof TNonspecificLiteralString + && !$atomic_key_type instanceof TNonspecificLiteralInt + && !$atomic_key_type instanceof TFalse + && !$atomic_key_type instanceof TTrue ) { return false; } @@ -1664,13 +1698,13 @@ public function getLiteralInts(): array } /** - * @return array + * @return array */ public function getRangeInts(): array { $ranges = []; foreach ($this->getAtomicTypes() as $atomic) { - if ($atomic instanceof Type\Atomic\TIntRange) { + if ($atomic instanceof TIntRange) { $ranges[$atomic->getKey()] = $atomic; } } diff --git a/src/functions.php b/src/functions.php index 4145e43b30a..a44e53cc7e1 100644 --- a/src/functions.php +++ b/src/functions.php @@ -1,4 +1,5 @@ */ + /** + * @psalm-pure + * @return list + */ public static function cases(): array; } interface BackedEnum { public readonly int|string $value; + + /** + * @psalm-pure + */ public static function from(string|int $value): static; + + /** + * @psalm-pure + */ public static function tryFrom(string|int $value): ?static; } @@ -27,12 +38,22 @@ namespace { class ReflectionEnumUnitCase extends ReflectionClassConstant implements Reflector { + /** + * @psalm-pure + */ public function getEnum(): ReflectionEnum; + + /** + * @psalm-pure + */ public function getValue(): UnitEnum; } class ReflectionEnumBackedCase extends ReflectionEnumUnitCase implements Reflector { + /** + * @psalm-pure + */ public function getBackingValue(): int|string; } } diff --git a/stubs/Reflection.phpstub b/stubs/Reflection.phpstub index 6635570394b..ece7ca8ae6d 100644 --- a/stubs/Reflection.phpstub +++ b/stubs/Reflection.phpstub @@ -51,7 +51,7 @@ class ReflectionClass implements Reflector { * @since 8.0 * @template TClass as object * @param class-string|null $name - * @return array> + * @return ($name is null ? array> : array>) */ public function getAttributes(?string $name = null, int $flags = 0): array {} } @@ -62,7 +62,7 @@ class ReflectionFunction implements Reflector * @since 8.0 * @template TClass as object * @param class-string|null $name - * @return array> + * @return ($name is null ? array> : array>) */ public function getAttributes(?string $name = null, int $flags = 0): array {} } @@ -73,7 +73,7 @@ class ReflectionProperty implements Reflector * @since 8.0 * @template TClass as object * @param class-string|null $name - * @return array> + * @return ($name is null ? array> : array>) */ public function getAttributes(?string $name = null, int $flags = 0): array {} @@ -96,9 +96,11 @@ class ReflectionMethod implements Reflector * @since 8.0 * @template TClass as object * @param class-string|null $name - * @return array> + * @return ($name is null ? array> : array>) */ public function getAttributes(?string $name = null, int $flags = 0): array {} + + public function isStatic(): bool {} } class ReflectionClassConstant @@ -107,7 +109,7 @@ class ReflectionClassConstant * @since 8.0 * @template TClass as object * @param class-string|null $name - * @return array> + * @return ($name is null ? array> : array>) */ public function getAttributes(?string $name = null, int $flags = 0): array {} } diff --git a/stubs/ext-ds.phpstub b/stubs/ext-ds.phpstub index fbf21834a50..04eef4dc4c5 100644 --- a/stubs/ext-ds.phpstub +++ b/stubs/ext-ds.phpstub @@ -1113,6 +1113,15 @@ final class Set implements Collection, ArrayAccess { } + /** + * @template TNewValue + * @param callable(TValue): TNewValue $callback + * @return Set + */ + public function map(callable $callback): Set + { + } + /** * @template TValue2 * @param iterable $values diff --git a/stubs/soap.phpstub b/stubs/soap.phpstub index 6169ca4c8ec..0eb107823ef 100644 --- a/stubs/soap.phpstub +++ b/stubs/soap.phpstub @@ -189,7 +189,7 @@ class SoapClient { /** * Returns last SOAP request * @link https://php.net/manual/en/soapclient.getlastrequest.php - * @return string The last SOAP request, as an XML string. + * @return string|null The last SOAP request, as an XML string. * @since 5.0.1 */ public function __getLastRequest () {} @@ -197,7 +197,7 @@ class SoapClient { /** * Returns last SOAP response * @link https://php.net/manual/en/soapclient.getlastresponse.php - * @return string The last SOAP response, as an XML string. + * @return string|null The last SOAP response, as an XML string. * @since 5.0.1 */ public function __getLastResponse () {} @@ -205,7 +205,7 @@ class SoapClient { /** * Returns the SOAP headers from the last request * @link https://php.net/manual/en/soapclient.getlastrequestheaders.php - * @return string The last SOAP request headers. + * @return string|null The last SOAP request headers. * @since 5.0.1 */ public function __getLastRequestHeaders () {} @@ -213,7 +213,7 @@ class SoapClient { /** * Returns the SOAP headers from the last response * @link https://php.net/manual/en/soapclient.getlastresponseheaders.php - * @return string The last SOAP response headers. + * @return string|null The last SOAP response headers. * @since 5.0.1 */ public function __getLastResponseHeaders () {} @@ -221,7 +221,7 @@ class SoapClient { /** * Returns list of available SOAP functions * @link https://php.net/manual/en/soapclient.getfunctions.php - * @return array The array of SOAP function prototypes, detailing the return type, + * @return array|null The array of SOAP function prototypes, detailing the return type, * the function name and type-hinted parameters. * @since 5.0.1 */ @@ -230,7 +230,7 @@ class SoapClient { /** * Returns a list of SOAP types * @link https://php.net/manual/en/soapclient.gettypes.php - * @return array The array of SOAP types, detailing all structures and types. + * @return array|null The array of SOAP types, detailing all structures and types. * @since 5.0.1 */ public function __getTypes () {} diff --git a/tests/AlgebraTest.php b/tests/AlgebraTest.php index c34ad854d5f..ac5c42f0f79 100644 --- a/tests/AlgebraTest.php +++ b/tests/AlgebraTest.php @@ -1,4 +1,5 @@ analyzeFile('somefile.php', new Context()); } + public function testLessSpecificImplementedReturnTypeWithDocblockOnMultipleLines(): void + { + $this->expectException(CodeException::class); + $this->expectExceptionMessage('LessSpecificImplementedReturnType - somefile.php:5:'); + + $this->addFile( + 'somefile.php', + 'analyzeFile('somefile.php', new Context()); + } + + public function testLessSpecificImplementedReturnTypeWithDocblockOnMultipleLinesWithMultipleClasses(): void + { + $this->expectException(CodeException::class); + $this->expectExceptionMessage('LessSpecificImplementedReturnType - somefile.php:15:'); + + $this->addFile( + 'somefile.php', + 'analyzeFile('somefile.php', new Context()); + } + + public function testLessSpecificImplementedReturnTypeWithDescription(): void + { + $this->expectException(CodeException::class); + $this->expectExceptionMessage('LessSpecificImplementedReturnType - somefile.php:7:'); + + $this->addFile( + 'somefile.php', + 'analyzeFile('somefile.php', new Context()); + } + public function testPhpStormGenericsNoTypehint(): void { $this->expectException(CodeException::class); diff --git a/tests/ArgTest.php b/tests/ArgTest.php index 259d7cb2f57..c931d1243a6 100644 --- a/tests/ArgTest.php +++ b/tests/ArgTest.php @@ -1,4 +1,5 @@ [ + ' [ + '$_foo' => 'null|string', + '$_bar' => 'null|string', + ], + [], + '8.1' + ], ]; } diff --git a/tests/ArrayFunctionCallTest.php b/tests/ArrayFunctionCallTest.php index a2260ca3622..f504a6f0def 100644 --- a/tests/ArrayFunctionCallTest.php +++ b/tests/ArrayFunctionCallTest.php @@ -1,4 +1,5 @@ b->c); }', ], + 'assertOnKeyedArrayWithClassStringOffset' => [ + ' ""]; + + /** @var array $b */ + $b = []; + + $this->assertSame($a, $b); + } + + /** + * @template T + * @param T $expected + * @param mixed $actual + * @psalm-assert =T $actual + */ + public function assertSame($expected, $actual): void + { + return; + } + }', + ], ]; } diff --git a/tests/AssignmentTest.php b/tests/AssignmentTest.php index bc5f53e6230..59165389ef3 100644 --- a/tests/AssignmentTest.php +++ b/tests/AssignmentTest.php @@ -1,4 +1,5 @@ [ + 'getAttributes(); + ', + 'assertions' => [ + '$b' => 'array>', + ], + [], + '8.0' + ], 'convertKeyedArray' => [ ' [ + ' 'InvalidArgument', + ], ]; } } diff --git a/tests/CodebaseTest.php b/tests/CodebaseTest.php index 84ba487fd96..36dee856086 100644 --- a/tests/CodebaseTest.php +++ b/tests/CodebaseTest.php @@ -1,4 +1,5 @@ getFiletypeAnalyzers()[$extension] ?? null); self::assertNull($expectedExceptionCode, 'Expected exception code was not thrown'); } + + public function testTypeStatsForFileReporting(): void + { + $this->project_analyzer = $this->getProjectAnalyzerWithConfig( + Config::loadFromXML( + (string) getcwd(), + ' + + + + + + + + + + + + ' + ) + ); + + $config = $this->project_analyzer->getConfig(); + + $this->assertFalse($config->reportTypeStatsForFile(realpath('src/Psalm/Config') . DIRECTORY_SEPARATOR)); + $this->assertTrue($config->reportTypeStatsForFile(realpath('src/Psalm/Internal') . DIRECTORY_SEPARATOR)); + $this->assertTrue($config->reportTypeStatsForFile(realpath('src/Psalm/Issue') . DIRECTORY_SEPARATOR)); + $this->assertTrue($config->reportTypeStatsForFile(realpath('src/Psalm/Node') . DIRECTORY_SEPARATOR)); + $this->assertTrue($config->reportTypeStatsForFile(realpath('src/Psalm/Plugin') . DIRECTORY_SEPARATOR)); + $this->assertTrue($config->reportTypeStatsForFile(realpath('src/Psalm/Progress') . DIRECTORY_SEPARATOR)); + $this->assertTrue($config->reportTypeStatsForFile(realpath('src/Psalm/Report') . DIRECTORY_SEPARATOR)); + $this->assertTrue($config->reportTypeStatsForFile(realpath('src/Psalm/SourceControl') . DIRECTORY_SEPARATOR)); + } + + public function testStrictTypesForFileReporting(): void + { + $this->project_analyzer = $this->getProjectAnalyzerWithConfig( + Config::loadFromXML( + (string) getcwd(), + ' + + + + + + + + + + + + ' + ) + ); + + $config = $this->project_analyzer->getConfig(); + + $this->assertTrue($config->useStrictTypesForFile(realpath('src/Psalm/Config') . DIRECTORY_SEPARATOR)); + $this->assertFalse($config->useStrictTypesForFile(realpath('src/Psalm/Internal') . DIRECTORY_SEPARATOR)); + $this->assertFalse($config->useStrictTypesForFile(realpath('src/Psalm/Issue') . DIRECTORY_SEPARATOR)); + $this->assertFalse($config->useStrictTypesForFile(realpath('src/Psalm/Node') . DIRECTORY_SEPARATOR)); + $this->assertFalse($config->useStrictTypesForFile(realpath('src/Psalm/Plugin') . DIRECTORY_SEPARATOR)); + $this->assertFalse($config->useStrictTypesForFile(realpath('src/Psalm/Progress') . DIRECTORY_SEPARATOR)); + $this->assertFalse($config->useStrictTypesForFile(realpath('src/Psalm/Report') . DIRECTORY_SEPARATOR)); + $this->assertFalse($config->useStrictTypesForFile(realpath('src/Psalm/SourceControl') . DIRECTORY_SEPARATOR)); + } } diff --git a/tests/Config/CreatorTest.php b/tests/Config/CreatorTest.php index ed437917c9e..6be60df3988 100644 --- a/tests/Config/CreatorTest.php +++ b/tests/Config/CreatorTest.php @@ -1,4 +1,5 @@ getMethodNameLowercase(); if ($method_name_lowercase === 'magicmethod') { diff --git a/tests/Config/Plugin/Hook/FooPropertyProvider.php b/tests/Config/Plugin/Hook/FooPropertyProvider.php index 2bfb3a7c159..7b7e3e36dba 100644 --- a/tests/Config/Plugin/Hook/FooPropertyProvider.php +++ b/tests/Config/Plugin/Hook/FooPropertyProvider.php @@ -1,4 +1,5 @@ 'DeprecatedProperty', ], + 'deprecatedEnumCaseFetch' => [ + ' 'DeprecatedConstant', + [], + false, + '8.1', + ] ]; } } diff --git a/tests/DocCommentTest.php b/tests/DocCommentTest.php index b86fb1be12f..bd542069b51 100644 --- a/tests/DocCommentTest.php +++ b/tests/DocCommentTest.php @@ -1,4 +1,5 @@ [ + ' 'DeprecatedConstant', + [], + false, + '8.1', + ], ]; } } diff --git a/tests/ErrorBaselineTest.php b/tests/ErrorBaselineTest.php index 5d2eb81127e..bf4a49cd3c2 100644 --- a/tests/ErrorBaselineTest.php +++ b/tests/ErrorBaselineTest.php @@ -1,4 +1,5 @@ [ + 'analyzeFileForReport(); - $console_report_options = new Report\ReportOptions(); + $console_report_options = new ReportOptions(); $console_report_options->show_snippet = false; $console_report_options->use_color = true; $console_report_options->in_ci = true; diff --git a/tests/ReturnTypeTest.php b/tests/ReturnTypeTest.php index c2028dd487e..1241c9bd110 100644 --- a/tests/ReturnTypeTest.php +++ b/tests/ReturnTypeTest.php @@ -1,4 +1,5 @@ [ + 'getSingleAtomic(); } diff --git a/tests/TypeComparatorTest.php b/tests/TypeComparatorTest.php index db7d948b14f..714a786b32e 100644 --- a/tests/TypeComparatorTest.php +++ b/tests/TypeComparatorTest.php @@ -1,4 +1,5 @@ ['' => Type::getArray()], - 'K' => ['' => new Type\Union([ - new Type\Atomic\TTemplateKeyOf('T', 'fn-foo', Type::getMixed()) + 'K' => ['' => new Union([ + new TTemplateKeyOf('T', 'fn-foo', Type::getMixed()) ])], ] ) @@ -802,16 +809,16 @@ public function testEnum(): void { $docblock_type = Type::parseString('( \'foo\\\'with\' | "bar\"bar" | "baz" | "bat\\\\" | \'bang bang\' | 1 | 2 | 3 | 4.5)'); - $resolved_type = new Type\Union([ - new Type\Atomic\TLiteralString('foo\'with'), - new Type\Atomic\TLiteralString('bar"bar'), - new Type\Atomic\TLiteralString('baz'), - new Type\Atomic\TLiteralString('bat\\'), - new Type\Atomic\TLiteralString('bang bang'), - new Type\Atomic\TLiteralInt(1), - new Type\Atomic\TLiteralInt(2), - new Type\Atomic\TLiteralInt(3), - new Type\Atomic\TLiteralFloat(4.5), + $resolved_type = new Union([ + new TLiteralString('foo\'with'), + new TLiteralString('bar"bar'), + new TLiteralString('baz'), + new TLiteralString('bat\\'), + new TLiteralString('bang bang'), + new TLiteralInt(1), + new TLiteralInt(2), + new TLiteralInt(3), + new TLiteralFloat(4.5), ]); $this->assertSame($resolved_type->getId(), $docblock_type->getId()); @@ -821,30 +828,30 @@ public function testEmptyString(): void { $docblock_type = Type::parseString('""|"admin"|"fun"'); - $resolved_type = new Type\Union([ - new Type\Atomic\TLiteralString(''), - new Type\Atomic\TLiteralString('admin'), - new Type\Atomic\TLiteralString('fun'), + $resolved_type = new Union([ + new TLiteralString(''), + new TLiteralString('admin'), + new TLiteralString('fun'), ]); $this->assertSame($resolved_type->getId(), $docblock_type->getId()); $docblock_type = Type::parseString('"admin"|""|"fun"'); - $resolved_type = new Type\Union([ - new Type\Atomic\TLiteralString('admin'), - new Type\Atomic\TLiteralString(''), - new Type\Atomic\TLiteralString('fun'), + $resolved_type = new Union([ + new TLiteralString('admin'), + new TLiteralString(''), + new TLiteralString('fun'), ]); $this->assertSame($resolved_type->getId(), $docblock_type->getId()); $docblock_type = Type::parseString('"admin"|"fun"|""'); - $resolved_type = new Type\Union([ - new Type\Atomic\TLiteralString('admin'), - new Type\Atomic\TLiteralString('fun'), - new Type\Atomic\TLiteralString(''), + $resolved_type = new Union([ + new TLiteralString('admin'), + new TLiteralString('fun'), + new TLiteralString(''), ]); $this->assertSame($resolved_type->getId(), $docblock_type->getId()); @@ -854,16 +861,16 @@ public function testEnumWithoutSpaces(): void { $docblock_type = Type::parseString('\'foo\\\'with\'|"bar\"bar"|"baz"|"bat\\\\"|\'bang bang\'|1|2|3|4.5'); - $resolved_type = new Type\Union([ - new Type\Atomic\TLiteralString('foo\'with'), - new Type\Atomic\TLiteralString('bar"bar'), - new Type\Atomic\TLiteralString('baz'), - new Type\Atomic\TLiteralString('bat\\'), - new Type\Atomic\TLiteralString('bang bang'), - new Type\Atomic\TLiteralInt(1), - new Type\Atomic\TLiteralInt(2), - new Type\Atomic\TLiteralInt(3), - new Type\Atomic\TLiteralFloat(4.5), + $resolved_type = new Union([ + new TLiteralString('foo\'with'), + new TLiteralString('bar"bar'), + new TLiteralString('baz'), + new TLiteralString('bat\\'), + new TLiteralString('bang bang'), + new TLiteralInt(1), + new TLiteralInt(2), + new TLiteralInt(3), + new TLiteralFloat(4.5), ]); $this->assertSame($resolved_type->getId(), $docblock_type->getId()); @@ -906,11 +913,11 @@ public function testEnumWithClassConstants(): void { $docblock_type = Type::parseString('("baz" | One2::TWO_THREE | Foo::BAR_BAR | Bat\Bar::BAZ_BAM)'); - $resolved_type = new Type\Union([ - new Type\Atomic\TLiteralString('baz'), - new Type\Atomic\TClassConstant('One2', 'TWO_THREE'), - new Type\Atomic\TClassConstant('Foo', 'BAR_BAR'), - new Type\Atomic\TClassConstant('Bat\\Bar', 'BAZ_BAM'), + $resolved_type = new Union([ + new TLiteralString('baz'), + new TClassConstant('One2', 'TWO_THREE'), + new TClassConstant('Foo', 'BAR_BAR'), + new TClassConstant('Bat\\Bar', 'BAZ_BAM'), ]); $this->assertSame($resolved_type->getId(), $docblock_type->getId()); diff --git a/tests/TypeReconciliation/ArrayKeyExistsTest.php b/tests/TypeReconciliation/ArrayKeyExistsTest.php index 4c33dc8c0f8..0f8bfe16498 100644 --- a/tests/TypeReconciliation/ArrayKeyExistsTest.php +++ b/tests/TypeReconciliation/ArrayKeyExistsTest.php @@ -1,4 +1,5 @@ [ + 'removeCallableString' => [ ' [ + 'statements_analyzer, diff --git a/tests/TypeReconciliation/RedundantConditionTest.php b/tests/TypeReconciliation/RedundantConditionTest.php index 9f15b294cc0..1ba00f4eb48 100644 --- a/tests/TypeReconciliation/RedundantConditionTest.php +++ b/tests/TypeReconciliation/RedundantConditionTest.php @@ -1,4 +1,5 @@