diff --git a/src/Turbo/TurboExtensionEnabler.php b/src/Turbo/TurboExtensionEnabler.php index 88c83cac563..0100a471b3b 100644 --- a/src/Turbo/TurboExtensionEnabler.php +++ b/src/Turbo/TurboExtensionEnabler.php @@ -33,7 +33,7 @@ final class TurboExtensionEnabler { - public const EXPECTED_EXTENSION_VERSION = '28c29b8'; + public const EXPECTED_EXTENSION_VERSION = '02da319'; private static bool $active = false; diff --git a/src/Type/BenevolentUnionType.php b/src/Type/BenevolentUnionType.php index 5a44af5e202..4fe27d46760 100644 --- a/src/Type/BenevolentUnionType.php +++ b/src/Type/BenevolentUnionType.php @@ -96,6 +96,11 @@ protected function pickFromTypes( return $values; } + public function unsetOffset(Type $offsetType): Type + { + return $this->unionTypes(static fn (Type $type): Type => $type->unsetOffset($offsetType)); + } + public function getOffsetValueType(Type $offsetType): Type { $types = []; diff --git a/src/Type/BooleanType.php b/src/Type/BooleanType.php index f7062440278..73a5084cd8c 100644 --- a/src/Type/BooleanType.php +++ b/src/Type/BooleanType.php @@ -49,6 +49,12 @@ public function getConstantStrings(): array return []; } + /** unset() of an offset leaves false as it is (deprecated since PHP 8.1) and throws on true. */ + public function unsetOffset(Type $offsetType): Type + { + return new ConstantBooleanType(false); + } + public function getConstantScalarTypes(): array { return [new ConstantBooleanType(true), new ConstantBooleanType(false)]; diff --git a/src/Type/Constant/ConstantBooleanType.php b/src/Type/Constant/ConstantBooleanType.php index 4c71f00b2d1..8ee8698f3ec 100644 --- a/src/Type/Constant/ConstantBooleanType.php +++ b/src/Type/Constant/ConstantBooleanType.php @@ -137,6 +137,16 @@ public function isFalse(): TrinaryLogic return TrinaryLogic::createFromBoolean($this->value === false); } + public function unsetOffset(Type $offsetType): Type + { + /** unset() of an offset leaves false as it is (deprecated since PHP 8.1) and throws on true. see https://3v4l.org/mHHkL#veol */ + if ($this->value) { + return new ErrorType(); + } + + return $this; + } + public function generalize(GeneralizePrecision $precision): Type { return new BooleanType(); diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index 4edc065cd63..a658aae0aba 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -1004,9 +1004,36 @@ public function setExistingOffsetValueType(Type $offsetType, Type $valueType): T return $this->unionTypes(static fn (Type $type): Type => $type->setExistingOffsetValueType($offsetType, $valueType)); } + /** + * Members that cannot have an offset unset (true, int, string, ...) yield ErrorType. + * They are left out instead of turning the whole union into ErrorType. + */ public function unsetOffset(Type $offsetType): Type { - return $this->unionTypes(static fn (Type $type): Type => $type->unsetOffset($offsetType)); + $types = []; + $changed = false; + foreach ($this->types as $innerType) { + $newType = $innerType->unsetOffset($offsetType); + if ($newType instanceof ErrorType) { + $changed = true; + continue; + } + if ($newType !== $innerType) { + $changed = true; + } + + $types[] = $newType; + } + + if (count($types) === 0) { + return new ErrorType(); + } + + if (!$changed) { + return $this; + } + + return TypeCombinator::union(...$types); } public function getKeysArrayFiltered(Type $filterValueType, TrinaryLogic $strict): Type diff --git a/tests/PHPStan/Analyser/nsrt/unset-offset-on-union.php b/tests/PHPStan/Analyser/nsrt/unset-offset-on-union.php new file mode 100644 index 00000000000..90db9cbca10 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/unset-offset-on-union.php @@ -0,0 +1,151 @@ += 8.0 + +namespace UnsetOffsetOnUnion; + +use function PHPStan\Testing\assertNativeType; +use function PHPStan\Testing\assertType; + +class Foo +{ + + /** + * @param array{port?: int, path?: string}|false $parsed + */ + public function arrayOrFalse($parsed): void + { + unset($parsed['port']); + assertType('array{path?: string}|false', $parsed); + } + + /** + * @param array{port?: int, path?: string}|string $value + */ + public function arrayOrString($value): void + { + unset($value['port']); + assertType('array{path?: string}', $value); + } + + /** + * @param array{port?: int, path?: string}|null $value + */ + public function arrayOrNull($value): void + { + unset($value['port']); + assertType('array{path?: string}|null', $value); + } + + /** + * @param array|int|false $value + */ + public function generalArray($value): void + { + unset($value['port']); + assertType('array|false', $value); + } + + /** + * @param int|string $value + */ + public function nothingToUnset($value): void + { + unset($value['port']); + assertType('*ERROR*', $value); + } + + public function parseUrl(string $url): void + { + $parsed = parse_url($url); + unset($parsed['port']); + assertType('array{scheme?: string, host?: string, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|false', $parsed); + assertNativeType('array|false|null', $parsed); + } + + /** + * @param array|false $value + */ + public function nativeArrayOrFalse(array|false $value): void + { + unset($value['port']); + assertNativeType('array|false', $value); + } + + /** + * @param __benevolent $value + */ + public function benevolent($value): void + { + unset($value['port']); + assertType('(array{path?: string}|false)', $value); + } + + /** + * @param __benevolent $value + */ + public function benevolentStaysBenevolent($value): void + { + unset($value['port']); + assertType('(array{path?: string}|false|null)', $value); + } + + /** + * @param array{port?: int, path?: string}|bool $value + */ + public function arrayOrBool($value): void + { + unset($value['port']); + assertType('array{path?: string}|false', $value); + } + + /** + * @param array{port?: int, path?: string}|true $value + */ + public function arrayOrTrue($value): void + { + unset($value['port']); + assertType('array{path?: string}', $value); + } + + /** + * @param \ArrayAccess|false $value + */ + public function arrayAccessOrFalse($value): void + { + unset($value['port']); + assertType('ArrayAccess|false', $value); + } + + public function nativeArrayAccessOrFalse(\ArrayAccess|false $value): void + { + unset($value['port']); + assertNativeType('ArrayAccess|false', $value); + } + + /** + * @param \ArrayAccess|array{port?: int, path?: string}|false $value + */ + public function arrayAccessOrArrayOrFalse($value): void + { + unset($value['port']); + assertType('array{path?: string}|ArrayAccess|false', $value); + } + + /** + * @param \ArrayAccess|string $value + */ + public function arrayAccessOrString($value): void + { + unset($value['port']); + assertType('ArrayAccess', $value); + } + + /** + * @param \ArrayAccess|bool $value + */ + public function arrayAccessOrBool($value): void + { + unset($value['port']); + assertType('ArrayAccess|false', $value); + } + +} diff --git a/turbo-ext/src/BenevolentUnionType.cpp b/turbo-ext/src/BenevolentUnionType.cpp index 3163402d720..03145415118 100644 --- a/turbo-ext/src/BenevolentUnionType.cpp +++ b/turbo-ext/src/BenevolentUnionType.cpp @@ -7,12 +7,12 @@ * extends it in turn), not final, declaring only what the twin declares — * the constructor forwarding to the parent's, and the overrides that make * the union benevolent: unionResults() answering yes as soon as one member - * does, unionTypes()/getOffsetValueType()/traverse()/traverseSimultaneously()/ - * tryRemove()/filterTypes() keeping the result benevolent, describe() - * parenthesizing the parent's, pickFromTypes() consulting the criteria, - * isAcceptedBy() or'ing the members, the template maps combined with - * benevolentUnion(). Everything else is inherited from the parent's - * class entry, as the PHP class inherits it. + * does, unionTypes()/unsetOffset()/getOffsetValueType()/traverse()/ + * traverseSimultaneously()/tryRemove()/filterTypes() keeping the result + * benevolent, describe() parenthesizing the parent's, pickFromTypes() + * consulting the criteria, isAcceptedBy() or'ing the members, the template + * maps combined with benevolentUnion(). Everything else is inherited from + * the parent's class entry, as the PHP class inherits it. * * The logic lives in the BenevolentUnionType handle class below, mirroring * src/Type/BenevolentUnionType.php method for method; the parent:: calls go @@ -183,6 +183,19 @@ class BenevolentUnionType return zv::Val(std::move(values)); } + /* $this->unionTypes(static fn (Type $type): Type => $type->unsetOffset($offsetType)): + * the native body when unionTypes() is this class's own, a PHP + * subclass's override through a callable otherwise; UNDEF = pending + * exception */ + zv::Val unsetOffset(zval *offsetType) const + { + UnionMemberOp op = UnionMemberOp::call(PT_LC("unsetoffset"), 1, offsetType); + if (EXPECTED(pt_type_method_is(self, PT_LC("uniontypes"), buUnionTypes))) return unionTypes(op); + zv::Val callback = pt_union_op_callback(op); + if (UNEXPECTED(callback.isUndef())) return zv::Val(); + return callType(self, PT_LC("uniontypes"), 1, callback.raw()); + } + /* the benevolent union of the members' offset value types that are no * ErrorType, an ErrorType when none is; UNDEF = pending exception */ zv::Val getOffsetValueType(zval *offsetType) const @@ -520,6 +533,8 @@ PT_MINIT_REGISTRATION(pt_register_benevolent_union_type) cls.method(sigs::unionTypes, buUnionTypes); cls.method(sigs::pickFromTypes, buPickFromTypes); + cls.method<&BenevolentUnionType::unsetOffset, zp::Obj>(sigs::unsetOffset); + cls.method<&BenevolentUnionType::getOffsetValueType, zp::Obj>(sigs::getOffsetValueType); cls.op(); diff --git a/turbo-ext/src/BooleanType.cpp b/turbo-ext/src/BooleanType.cpp index 64c6f7c7550..7d728a70dad 100644 --- a/turbo-ext/src/BooleanType.cpp +++ b/turbo-ext/src/BooleanType.cpp @@ -28,6 +28,15 @@ class BooleanType static zv::Val getConstantStrings() { return zv::Val(zv::Arr::empty()); } + /* new ConstantBooleanType(false): unset() of an offset leaves false as + * it is and throws on true; UNDEF = pending exception */ + static zv::Val unsetOffset() + { + zval falseType; + if (UNEXPECTED(!pt_constant_boolean_type_new(&falseType, false))) return zv::Val(); + return zv::Val::adopt(falseType); + } + /* UNDEF = pending exception */ static zv::Val getConstantScalarTypes() { return bothConstants(); } @@ -244,6 +253,12 @@ PT_MINIT_REGISTRATION(pt_register_boolean_type) cls.method<&BooleanType::getConstantStrings>(sigs::getConstantStrings); + cls.method(sigs::unsetOffset, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *offsetType; + if (!zp::parse(execute_data, offsetType)) RETURN_THROWS(); + PT_RETURN_VAL(BooleanType::unsetOffset()); + }); + cls.method<&BooleanType::getConstantScalarTypes>(sigs::getConstantScalarTypes); cls.method<&BooleanType::getConstantScalarValues>(sigs::getConstantScalarValues); @@ -335,8 +350,9 @@ PT_MINIT_REGISTRATION(pt_register_boolean_type) cls.op(PT_OP_HAS_TEMPLATE_OR_LATE_RESOLVABLE_TYPE, PT_OP_LAMBDA { return zv::Val::boolean(BooleanType::hasTemplateOrLateResolvableType()); }); /* the traits, in the twin's `use` order; the class body above wins over - * every name it declares (getConstantStrings, getConstantScalarTypes, - * getConstantScalarValues, isNull, isTrue, isFalse, isBoolean) */ + * every name it declares (getConstantStrings, unsetOffset, + * getConstantScalarTypes, getConstantScalarValues, isNull, isTrue, + * isFalse, isBoolean) */ ptdecl::BooleanType::registerTraits(cls); cls.shadow(&pt_ce_boolean_type); diff --git a/turbo-ext/src/ConstantBooleanType.cpp b/turbo-ext/src/ConstantBooleanType.cpp index 81ed2a8bcca..625d05a67b9 100644 --- a/turbo-ext/src/ConstantBooleanType.cpp +++ b/turbo-ext/src/ConstantBooleanType.cpp @@ -193,6 +193,16 @@ class ConstantBooleanType return v ? PT_TRI_NO : PT_TRI_YES; } + /* an ErrorType for true, $this for false: unset() of an offset throws on + * true and leaves false as it is; UNDEF = pending exception */ + zv::Val unsetOffset() const + { + bool v = false; + if (UNEXPECTED(!value(v))) return zv::Val(); + if (v) return pt_type_new_error_type(); + return thisValue(); + } + /* new BooleanType() */ static zv::Val generalize() { @@ -324,6 +334,12 @@ PT_MINIT_REGISTRATION(pt_register_constant_boolean_type) PT_RETURN_TRINARY_OR_THROW(PT_THIS.isFalse()); }); + cls.method(sigs::unsetOffset, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *offsetType; + if (!zp::parse(execute_data, offsetType)) RETURN_THROWS(); + PT_RETURN_VAL(PT_THIS.unsetOffset()); + }); + cls.method(sigs::generalize, [](INTERNAL_FUNCTION_PARAMETERS) { zval *precision; if (!zp::parse(execute_data, precision)) RETURN_THROWS(); diff --git a/turbo-ext/src/UnionType.cpp b/turbo-ext/src/UnionType.cpp index ac355c53dfc..a69801c24c1 100644 --- a/turbo-ext/src/UnionType.cpp +++ b/turbo-ext/src/UnionType.cpp @@ -1435,7 +1435,35 @@ class UnionType return unionTypes(UnionMemberOp::call(PT_LC("setexistingoffsetvaluetype"), 2, args)); } - zv::Val unsetOffset(zval *offsetType) const { return unionTypes(UnionMemberOp::call(PT_LC("unsetoffset"), 1, offsetType)); } + /* the members' unsetOffset() results with the ErrorTypes left out: $this + * when nothing changed, an ErrorType when nothing is left, their union + * otherwise; UNDEF = pending exception */ + zv::Val unsetOffset(zval *offsetType) const + { + zval *types = this->types(); + if (UNEXPECTED(types == NULL)) return zv::Val(); + zv::Val typesCopy = zv::Val::copyOf(zv::Ref(types)); + zv::Arr newTypes = zv::Arr::create(zv::ArrRef(typesCopy.raw()).size()); + bool changed = false; + for (zv::ArrayEntry entry : zv::ArrRef(typesCopy.raw())) { + zend_object *innerType = entry.value().deref().asObject(); + zv::Val newType = callType(innerType, PT_LC("unsetoffset"), 1, offsetType); + if (UNEXPECTED(newType.isUndef())) return zv::Val(); + bool isError; + if (UNEXPECTED(!isInstance(newType.raw(), pt_ce_error_type, isError))) return zv::Val(); + if (isError) { + changed = true; + continue; + } + if (Z_OBJ_P(newType.raw()) != innerType) { + changed = true; + } + newTypes.push(std::move(newType)); + } + if (zend_hash_num_elements(newTypes.table()) == 0) return errorType(); + if (!changed) return thisValue(); + return combinatorUnion(newTypes.table()); + } zv::Val getKeysArrayFiltered(zval *filterValueType, zval *strict) const { diff --git a/turbo-ext/src/generated/BenevolentUnionType.h b/turbo-ext/src/generated/BenevolentUnionType.h index 196e687b237..719ed78d270 100644 --- a/turbo-ext/src/generated/BenevolentUnionType.h +++ b/turbo-ext/src/generated/BenevolentUnionType.h @@ -41,23 +41,24 @@ inline constexpr char strings[] = "criteria\0" /* 170 */ "pickFromTypes\0" /* 179 */ "offsetType\0" /* 193 */ - "getOffsetValueType\0" /* 204 */ - "getResult\0" /* 223 */ - "PHPStan\\TrinaryLogic\0" /* 233 */ - "unionResults\0" /* 254 */ - "acceptingType\0" /* 267 */ - "strictTypes\0" /* 281 */ - "PHPStan\\Type\\AcceptsResult\0" /* 293 */ - "isAcceptedBy\0" /* 320 */ - "receivedType\0" /* 333 */ - "PHPStan\\Type\\Generic\\TemplateTypeMap\0" /* 346 */ - "inferTemplateTypes\0" /* 383 */ - "templateType\0" /* 402 */ - "inferTemplateTypesOn\0" /* 415 */ - "cb\0" /* 436 */ - "traverse\0" /* 439 */ - "right\0" /* 448 */ - "traverseSimultaneously"; /* 454 */ + "unsetOffset\0" /* 204 */ + "getOffsetValueType\0" /* 216 */ + "getResult\0" /* 235 */ + "PHPStan\\TrinaryLogic\0" /* 245 */ + "unionResults\0" /* 266 */ + "acceptingType\0" /* 279 */ + "strictTypes\0" /* 293 */ + "PHPStan\\Type\\AcceptsResult\0" /* 305 */ + "isAcceptedBy\0" /* 332 */ + "receivedType\0" /* 345 */ + "PHPStan\\Type\\Generic\\TemplateTypeMap\0" /* 358 */ + "inferTemplateTypes\0" /* 395 */ + "templateType\0" /* 414 */ + "inferTemplateTypesOn\0" /* 427 */ + "cb\0" /* 448 */ + "traverse\0" /* 451 */ + "right\0" /* 460 */ + "traverseSimultaneously"; /* 466 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, MAY_BE_ARRAY), /* __construct $types */ reg::packed(6, MAY_BE_BOOL, reg::NoString, false, false, 17), /* __construct $normalized */ @@ -72,21 +73,23 @@ inline constexpr reg::PackedArg args[] = { reg::packed(160, MAY_BE_CALLABLE), /* pickFromTypes $getValues */ reg::packed(170, MAY_BE_CALLABLE), /* pickFromTypes $criteria */ reg::packed(44, MAY_BE_ARRAY), /* pickFromTypes return */ + reg::packed(193, 0, 45), /* unsetOffset $offsetType */ + reg::packed(44, 0, 45), /* unsetOffset return */ reg::packed(193, 0, 45), /* getOffsetValueType $offsetType */ reg::packed(44, 0, 45), /* getOffsetValueType return */ - reg::packed(223, MAY_BE_CALLABLE), /* unionResults $getResult */ - reg::packed(44, 0, 233), /* unionResults return */ - reg::packed(267, 0, 45), /* isAcceptedBy $acceptingType */ - reg::packed(281, MAY_BE_BOOL), /* isAcceptedBy $strictTypes */ - reg::packed(44, 0, 293), /* isAcceptedBy return */ - reg::packed(333, 0, 45), /* inferTemplateTypes $receivedType */ - reg::packed(44, 0, 346), /* inferTemplateTypes return */ - reg::packed(402, 0, 45), /* inferTemplateTypesOn $templateType */ - reg::packed(44, 0, 346), /* inferTemplateTypesOn return */ - reg::packed(436, MAY_BE_CALLABLE), /* traverse $cb */ + reg::packed(235, MAY_BE_CALLABLE), /* unionResults $getResult */ + reg::packed(44, 0, 245), /* unionResults return */ + reg::packed(279, 0, 45), /* isAcceptedBy $acceptingType */ + reg::packed(293, MAY_BE_BOOL), /* isAcceptedBy $strictTypes */ + reg::packed(44, 0, 305), /* isAcceptedBy return */ + reg::packed(345, 0, 45), /* inferTemplateTypes $receivedType */ + reg::packed(44, 0, 358), /* inferTemplateTypes return */ + reg::packed(414, 0, 45), /* inferTemplateTypesOn $templateType */ + reg::packed(44, 0, 358), /* inferTemplateTypesOn return */ + reg::packed(448, MAY_BE_CALLABLE), /* traverse $cb */ reg::packed(44, 0, 45), /* traverse return */ - reg::packed(448, 0, 45), /* traverseSimultaneously $right */ - reg::packed(436, MAY_BE_CALLABLE), /* traverseSimultaneously $cb */ + reg::packed(460, 0, 45), /* traverseSimultaneously $right */ + reg::packed(448, MAY_BE_CALLABLE), /* traverseSimultaneously $cb */ reg::packed(44, 0, 45), /* traverseSimultaneously return */ }; using Sig = reg::Sig; @@ -100,13 +103,14 @@ inline constexpr sigtab::Sig tryRemove = { { 88 /* tryRemove */, 1, 4, 1, 5, ZEN inline constexpr sigtab::Sig describe = { { 132 /* describe */, 1, 6, 1, 7, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig unionTypes = { { 149 /* unionTypes */, 1, 8, 1, 9, ZEND_ACC_PROTECTED } }; inline constexpr sigtab::Sig pickFromTypes = { { 179 /* pickFromTypes */, 2, 10, 2, 12, ZEND_ACC_PROTECTED } }; -inline constexpr sigtab::Sig getOffsetValueType = { { 204 /* getOffsetValueType */, 1, 13, 1, 14, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig unionResults = { { 254 /* unionResults */, 1, 15, 1, 16, ZEND_ACC_PROTECTED } }; -inline constexpr sigtab::Sig isAcceptedBy = { { 320 /* isAcceptedBy */, 2, 17, 2, 19, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig inferTemplateTypes = { { 383 /* inferTemplateTypes */, 1, 20, 1, 21, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig inferTemplateTypesOn = { { 415 /* inferTemplateTypesOn */, 1, 22, 1, 23, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig traverse = { { 439 /* traverse */, 1, 24, 1, 25, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig traverseSimultaneously = { { 454 /* traverseSimultaneously */, 2, 26, 2, 28, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig unsetOffset = { { 204 /* unsetOffset */, 1, 13, 1, 14, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getOffsetValueType = { { 216 /* getOffsetValueType */, 1, 15, 1, 16, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig unionResults = { { 266 /* unionResults */, 1, 17, 1, 18, ZEND_ACC_PROTECTED } }; +inline constexpr sigtab::Sig isAcceptedBy = { { 332 /* isAcceptedBy */, 2, 19, 2, 21, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig inferTemplateTypes = { { 395 /* inferTemplateTypes */, 1, 22, 1, 23, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig inferTemplateTypesOn = { { 427 /* inferTemplateTypesOn */, 1, 24, 1, 25, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig traverse = { { 451 /* traverse */, 1, 26, 1, 27, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig traverseSimultaneously = { { 466 /* traverseSimultaneously */, 2, 28, 2, 30, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::BenevolentUnionType diff --git a/turbo-ext/src/generated/BooleanType.h b/turbo-ext/src/generated/BooleanType.h index 4122ecd2c9f..5acc55ea575 100644 --- a/turbo-ext/src/generated/BooleanType.h +++ b/turbo-ext/src/generated/BooleanType.h @@ -40,75 +40,79 @@ inline constexpr char strings[] = "__construct\0" /* 0 */ "\0" /* 12 */ "getConstantStrings\0" /* 13 */ - "getConstantScalarTypes\0" /* 32 */ - "getConstantScalarValues\0" /* 55 */ - "level\0" /* 79 */ - "PHPStan\\Type\\VerbosityLevel\0" /* 85 */ - "describe\0" /* 113 */ - "PHPStan\\Type\\Type\0" /* 122 */ - "toNumber\0" /* 140 */ - "toBitwiseNotType\0" /* 149 */ - "toAbsoluteNumber\0" /* 166 */ - "toString\0" /* 183 */ - "toInteger\0" /* 192 */ - "toFloat\0" /* 202 */ - "toArray\0" /* 210 */ - "toArrayKey\0" /* 218 */ - "strictTypes\0" /* 229 */ - "toCoercedArgumentType\0" /* 241 */ - "PHPStan\\TrinaryLogic\0" /* 263 */ - "isOffsetAccessLegal\0" /* 284 */ - "isNull\0" /* 304 */ - "isTrue\0" /* 311 */ - "isFalse\0" /* 318 */ - "isBoolean\0" /* 326 */ - "isScalar\0" /* 336 */ - "type\0" /* 345 */ - "phpVersion\0" /* 350 */ - "PHPStan\\Php\\PhpVersion\0" /* 361 */ - "PHPStan\\Type\\BooleanType\0" /* 384 */ - "looseCompare\0" /* 409 */ - "typeToRemove\0" /* 422 */ - "tryRemove\0" /* 435 */ - "getFiniteTypes\0" /* 445 */ - "exponent\0" /* 460 */ - "exponentiate\0" /* 469 */ - "PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode\0" /* 482 */ - "toPhpDocNode\0" /* 521 */ - "toTrinaryLogic\0" /* 534 */ - "hasTemplateOrLateResolvableType"; /* 549 */ + "offsetType\0" /* 32 */ + "PHPStan\\Type\\Type\0" /* 43 */ + "unsetOffset\0" /* 61 */ + "getConstantScalarTypes\0" /* 73 */ + "getConstantScalarValues\0" /* 96 */ + "level\0" /* 120 */ + "PHPStan\\Type\\VerbosityLevel\0" /* 126 */ + "describe\0" /* 154 */ + "toNumber\0" /* 163 */ + "toBitwiseNotType\0" /* 172 */ + "toAbsoluteNumber\0" /* 189 */ + "toString\0" /* 206 */ + "toInteger\0" /* 215 */ + "toFloat\0" /* 225 */ + "toArray\0" /* 233 */ + "toArrayKey\0" /* 241 */ + "strictTypes\0" /* 252 */ + "toCoercedArgumentType\0" /* 264 */ + "PHPStan\\TrinaryLogic\0" /* 286 */ + "isOffsetAccessLegal\0" /* 307 */ + "isNull\0" /* 327 */ + "isTrue\0" /* 334 */ + "isFalse\0" /* 341 */ + "isBoolean\0" /* 349 */ + "isScalar\0" /* 359 */ + "type\0" /* 368 */ + "phpVersion\0" /* 373 */ + "PHPStan\\Php\\PhpVersion\0" /* 384 */ + "PHPStan\\Type\\BooleanType\0" /* 407 */ + "looseCompare\0" /* 432 */ + "typeToRemove\0" /* 445 */ + "tryRemove\0" /* 458 */ + "getFiniteTypes\0" /* 468 */ + "exponent\0" /* 483 */ + "exponentiate\0" /* 492 */ + "PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode\0" /* 505 */ + "toPhpDocNode\0" /* 544 */ + "toTrinaryLogic\0" /* 557 */ + "hasTemplateOrLateResolvableType"; /* 572 */ inline constexpr reg::PackedArg args[] = { reg::packed(12, MAY_BE_ARRAY), /* getConstantStrings return */ + reg::packed(32, 0, 43), /* unsetOffset $offsetType */ + reg::packed(12, 0, 43), /* unsetOffset return */ reg::packed(12, MAY_BE_ARRAY), /* getConstantScalarTypes return */ reg::packed(12, MAY_BE_ARRAY), /* getConstantScalarValues return */ - reg::packed(79, 0, 85), /* describe $level */ + reg::packed(120, 0, 126), /* describe $level */ reg::packed(12, MAY_BE_STRING), /* describe return */ - reg::packed(12, 0, 122), /* toNumber return */ - reg::packed(12, 0, 122), /* toBitwiseNotType return */ - reg::packed(12, 0, 122), /* toAbsoluteNumber return */ - reg::packed(12, 0, 122), /* toString return */ - reg::packed(12, 0, 122), /* toInteger return */ - reg::packed(12, 0, 122), /* toFloat return */ - reg::packed(12, 0, 122), /* toArray return */ - reg::packed(12, 0, 122), /* toArrayKey return */ - reg::packed(229, MAY_BE_BOOL), /* toCoercedArgumentType $strictTypes */ - reg::packed(12, 0, 122), /* toCoercedArgumentType return */ - reg::packed(12, 0, 263), /* isOffsetAccessLegal return */ - reg::packed(12, 0, 263), /* isNull return */ - reg::packed(12, 0, 263), /* isTrue return */ - reg::packed(12, 0, 263), /* isFalse return */ - reg::packed(12, 0, 263), /* isBoolean return */ - reg::packed(12, 0, 263), /* isScalar return */ - reg::packed(345, 0, 122), /* looseCompare $type */ - reg::packed(350, 0, 361), /* looseCompare $phpVersion */ - reg::packed(12, 0, 384), /* looseCompare return */ - reg::packed(422, 0, 122), /* tryRemove $typeToRemove */ - reg::packed(12, MAY_BE_NULL, 122), /* tryRemove return */ + reg::packed(12, 0, 43), /* toNumber return */ + reg::packed(12, 0, 43), /* toBitwiseNotType return */ + reg::packed(12, 0, 43), /* toAbsoluteNumber return */ + reg::packed(12, 0, 43), /* toString return */ + reg::packed(12, 0, 43), /* toInteger return */ + reg::packed(12, 0, 43), /* toFloat return */ + reg::packed(12, 0, 43), /* toArray return */ + reg::packed(12, 0, 43), /* toArrayKey return */ + reg::packed(252, MAY_BE_BOOL), /* toCoercedArgumentType $strictTypes */ + reg::packed(12, 0, 43), /* toCoercedArgumentType return */ + reg::packed(12, 0, 286), /* isOffsetAccessLegal return */ + reg::packed(12, 0, 286), /* isNull return */ + reg::packed(12, 0, 286), /* isTrue return */ + reg::packed(12, 0, 286), /* isFalse return */ + reg::packed(12, 0, 286), /* isBoolean return */ + reg::packed(12, 0, 286), /* isScalar return */ + reg::packed(368, 0, 43), /* looseCompare $type */ + reg::packed(373, 0, 384), /* looseCompare $phpVersion */ + reg::packed(12, 0, 407), /* looseCompare return */ + reg::packed(445, 0, 43), /* tryRemove $typeToRemove */ + reg::packed(12, MAY_BE_NULL, 43), /* tryRemove return */ reg::packed(12, MAY_BE_ARRAY), /* getFiniteTypes return */ - reg::packed(460, 0, 122), /* exponentiate $exponent */ - reg::packed(12, 0, 122), /* exponentiate return */ - reg::packed(12, 0, 482), /* toPhpDocNode return */ - reg::packed(12, 0, 263), /* toTrinaryLogic return */ + reg::packed(483, 0, 43), /* exponentiate $exponent */ + reg::packed(12, 0, 43), /* exponentiate return */ + reg::packed(12, 0, 505), /* toPhpDocNode return */ + reg::packed(12, 0, 286), /* toTrinaryLogic return */ reg::packed(12, MAY_BE_BOOL), /* hasTemplateOrLateResolvableType return */ }; using Sig = reg::Sig; @@ -118,31 +122,32 @@ using Sig = reg::Sig; namespace sig { inline constexpr sigtab::Sig __construct = { { 0 /* __construct */, 0, 0, 0, reg::NoArg, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig getConstantStrings = { { 13 /* getConstantStrings */, 0, 0, 0, 0, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getConstantScalarTypes = { { 32 /* getConstantScalarTypes */, 0, 1, 0, 1, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getConstantScalarValues = { { 55 /* getConstantScalarValues */, 0, 2, 0, 2, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig describe = { { 113 /* describe */, 1, 3, 1, 4, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toNumber = { { 140 /* toNumber */, 0, 5, 0, 5, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toBitwiseNotType = { { 149 /* toBitwiseNotType */, 0, 6, 0, 6, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toAbsoluteNumber = { { 166 /* toAbsoluteNumber */, 0, 7, 0, 7, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toString = { { 183 /* toString */, 0, 8, 0, 8, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toInteger = { { 192 /* toInteger */, 0, 9, 0, 9, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toFloat = { { 202 /* toFloat */, 0, 10, 0, 10, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toArray = { { 210 /* toArray */, 0, 11, 0, 11, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toArrayKey = { { 218 /* toArrayKey */, 0, 12, 0, 12, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toCoercedArgumentType = { { 241 /* toCoercedArgumentType */, 1, 13, 1, 14, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isOffsetAccessLegal = { { 284 /* isOffsetAccessLegal */, 0, 15, 0, 15, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isNull = { { 304 /* isNull */, 0, 16, 0, 16, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isTrue = { { 311 /* isTrue */, 0, 17, 0, 17, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isFalse = { { 318 /* isFalse */, 0, 18, 0, 18, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isBoolean = { { 326 /* isBoolean */, 0, 19, 0, 19, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isScalar = { { 336 /* isScalar */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig looseCompare = { { 409 /* looseCompare */, 2, 21, 2, 23, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig tryRemove = { { 435 /* tryRemove */, 1, 24, 1, 25, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getFiniteTypes = { { 445 /* getFiniteTypes */, 0, 26, 0, 26, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig exponentiate = { { 469 /* exponentiate */, 1, 27, 1, 28, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toPhpDocNode = { { 521 /* toPhpDocNode */, 0, 29, 0, 29, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toTrinaryLogic = { { 534 /* toTrinaryLogic */, 0, 30, 0, 30, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasTemplateOrLateResolvableType = { { 549 /* hasTemplateOrLateResolvableType */, 0, 31, 0, 31, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig unsetOffset = { { 61 /* unsetOffset */, 1, 1, 1, 2, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getConstantScalarTypes = { { 73 /* getConstantScalarTypes */, 0, 3, 0, 3, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getConstantScalarValues = { { 96 /* getConstantScalarValues */, 0, 4, 0, 4, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig describe = { { 154 /* describe */, 1, 5, 1, 6, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toNumber = { { 163 /* toNumber */, 0, 7, 0, 7, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toBitwiseNotType = { { 172 /* toBitwiseNotType */, 0, 8, 0, 8, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toAbsoluteNumber = { { 189 /* toAbsoluteNumber */, 0, 9, 0, 9, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toString = { { 206 /* toString */, 0, 10, 0, 10, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toInteger = { { 215 /* toInteger */, 0, 11, 0, 11, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toFloat = { { 225 /* toFloat */, 0, 12, 0, 12, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toArray = { { 233 /* toArray */, 0, 13, 0, 13, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toArrayKey = { { 241 /* toArrayKey */, 0, 14, 0, 14, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toCoercedArgumentType = { { 264 /* toCoercedArgumentType */, 1, 15, 1, 16, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isOffsetAccessLegal = { { 307 /* isOffsetAccessLegal */, 0, 17, 0, 17, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isNull = { { 327 /* isNull */, 0, 18, 0, 18, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isTrue = { { 334 /* isTrue */, 0, 19, 0, 19, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isFalse = { { 341 /* isFalse */, 0, 20, 0, 20, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isBoolean = { { 349 /* isBoolean */, 0, 21, 0, 21, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isScalar = { { 359 /* isScalar */, 0, 22, 0, 22, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig looseCompare = { { 432 /* looseCompare */, 2, 23, 2, 25, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig tryRemove = { { 458 /* tryRemove */, 1, 26, 1, 27, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getFiniteTypes = { { 468 /* getFiniteTypes */, 0, 28, 0, 28, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig exponentiate = { { 492 /* exponentiate */, 1, 29, 1, 30, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toPhpDocNode = { { 544 /* toPhpDocNode */, 0, 31, 0, 31, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toTrinaryLogic = { { 557 /* toTrinaryLogic */, 0, 32, 0, 32, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasTemplateOrLateResolvableType = { { 572 /* hasTemplateOrLateResolvableType */, 0, 33, 0, 33, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::BooleanType diff --git a/turbo-ext/src/generated/ConstantBooleanType.h b/turbo-ext/src/generated/ConstantBooleanType.h index a1c444b8ff6..3020d8d189e 100644 --- a/turbo-ext/src/generated/ConstantBooleanType.h +++ b/turbo-ext/src/generated/ConstantBooleanType.h @@ -62,13 +62,15 @@ inline constexpr char strings[] = "PHPStan\\TrinaryLogic\0" /* 347 */ "isTrue\0" /* 368 */ "isFalse\0" /* 375 */ - "precision\0" /* 383 */ - "PHPStan\\Type\\GeneralizePrecision\0" /* 393 */ - "generalize\0" /* 426 */ - "PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode\0" /* 437 */ - "toPhpDocNode\0" /* 476 */ - "type\0" /* 489 */ - "looseCompare"; /* 494 */ + "offsetType\0" /* 383 */ + "unsetOffset\0" /* 394 */ + "precision\0" /* 406 */ + "PHPStan\\Type\\GeneralizePrecision\0" /* 416 */ + "generalize\0" /* 449 */ + "PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode\0" /* 460 */ + "toPhpDocNode\0" /* 499 */ + "type\0" /* 512 */ + "looseCompare"; /* 517 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, MAY_BE_BOOL), /* __construct $value */ reg::packed(18, MAY_BE_BOOL), /* getValue return */ @@ -94,10 +96,12 @@ inline constexpr reg::PackedArg args[] = { reg::packed(18, 0, 105), /* toCoercedArgumentType return */ reg::packed(18, 0, 347), /* isTrue return */ reg::packed(18, 0, 347), /* isFalse return */ - reg::packed(383, 0, 393), /* generalize $precision */ + reg::packed(383, 0, 105), /* unsetOffset $offsetType */ + reg::packed(18, 0, 105), /* unsetOffset return */ + reg::packed(406, 0, 416), /* generalize $precision */ reg::packed(18, 0, 105), /* generalize return */ - reg::packed(18, 0, 437), /* toPhpDocNode return */ - reg::packed(489, 0, 105), /* looseCompare $type */ + reg::packed(18, 0, 460), /* toPhpDocNode return */ + reg::packed(512, 0, 105), /* looseCompare $type */ reg::packed(71, 0, 82), /* looseCompare $phpVersion */ reg::packed(18, 0, 197), /* looseCompare return */ }; @@ -124,9 +128,10 @@ inline constexpr sigtab::Sig toArrayKey = { { 302 /* toArrayKey */, 0, 19, 0, 19 inline constexpr sigtab::Sig toCoercedArgumentType = { { 325 /* toCoercedArgumentType */, 1, 20, 1, 21, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig isTrue = { { 368 /* isTrue */, 0, 22, 0, 22, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig isFalse = { { 375 /* isFalse */, 0, 23, 0, 23, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig generalize = { { 426 /* generalize */, 1, 24, 1, 25, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig toPhpDocNode = { { 476 /* toPhpDocNode */, 0, 26, 0, 26, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig looseCompare = { { 494 /* looseCompare */, 2, 27, 2, 29, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig unsetOffset = { { 394 /* unsetOffset */, 1, 24, 1, 25, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig generalize = { { 449 /* generalize */, 1, 26, 1, 27, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig toPhpDocNode = { { 499 /* toPhpDocNode */, 0, 28, 0, 28, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig looseCompare = { { 517 /* looseCompare */, 2, 29, 2, 31, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::ConstantBooleanType diff --git a/turbo-ext/tests/type-family.php b/turbo-ext/tests/type-family.php index b3c2d16ab39..2ebe99882e0 100644 --- a/turbo-ext/tests/type-family.php +++ b/turbo-ext/tests/type-family.php @@ -3455,8 +3455,11 @@ public function getValuesArray(): \PHPStan\Type\Constant\ConstantArrayType 'unionBools' => new $union([new \PHPStan\Type\Constant\ConstantBooleanType(true), new \PHPStan\Type\Constant\ConstantBooleanType(false)]), 'unionArrays' => new $union([new \PHPStan\Type\ArrayType(new \PHPStan\Type\IntegerType(), new \PHPStan\Type\StringType()), new \PHPStan\Type\NullType()]), 'unionObjects' => new $union([new \PHPStan\Type\ObjectType(\stdClass::class), new \PHPStan\Type\ObjectType(\PHPStan\TrinaryLogic::class)]), + 'unionArrayOrFalse' => new $union([$compoundConstShape(['a' => new \PHPStan\Type\IntegerType(), 'b' => new \PHPStan\Type\StringType()], [0]), new \PHPStan\Type\Constant\ConstantBooleanType(false)]), + 'unionArrayOrString' => new $union([new \PHPStan\Type\ArrayType(new \PHPStan\Type\StringType(), new \PHPStan\Type\IntegerType()), new \PHPStan\Type\StringType()]), 'benevolent' => new $benevolent([new \PHPStan\Type\IntegerType(), new \PHPStan\Type\StringType()]), 'benevolentNullable' => new $benevolent([new \PHPStan\Type\IntegerType(), new \PHPStan\Type\StringType(), new \PHPStan\Type\NullType()]), + 'benevolentArrayOrFalse' => new $benevolent([new \PHPStan\Type\ArrayType(new \PHPStan\Type\StringType(), new \PHPStan\Type\IntegerType()), new \PHPStan\Type\Constant\ConstantBooleanType(false), new \PHPStan\Type\NullType()]), 'array' => new \PHPStan\Type\ArrayType(new \PHPStan\Type\MixedType(), new \PHPStan\Type\MixedType()), 'arrayIntString' => new \PHPStan\Type\ArrayType(new \PHPStan\Type\IntegerType(), new \PHPStan\Type\StringType()), 'arrayStringInt' => new \PHPStan\Type\ArrayType(new \PHPStan\Type\StringType(), new \PHPStan\Type\IntegerType()),