$types */ public function testValidTypeCombination(string $expected, array $types): void { $converted_types = []; foreach ($types as $type) { $converted_type = self::getAtomic($type); /** @psalm-suppress InaccessibleProperty */ $converted_type->from_docblock = true; $converted_types[] = $converted_type; } $this->assertSame( $expected, TypeCombiner::combine($converted_types)->getId(), ); $this->assertSame( $expected, TypeCombiner::combine(array_reverse($converted_types))->getId(), ); } public function providerValidCodeParse(): iterable { return [ 'multipleValuedArray' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' $_a */ function expectsTraversableOrArray($_a): void { } ', ], ]; } /** * @return array}> */ public function providerTestValidTypeCombination(): array { return [ 'complexArrayFallback1' => [ 'array{other_references: list|null, taint_trace: list>|null, ...}', [ 'array{other_references: list|null, taint_trace: null}&array', 'array{other_references: list|null, taint_trace: list>}&array', ], ], 'complexArrayFallback2' => [ 'list{0?: 0|a, 1?: 0|a, ...}', [ 'list', 'list{0, 0}', ], ], 'intOrString' => [ 'int|string', [ 'int', 'string', ], ], 'mixedOrNull' => [ 'mixed|null', [ 'mixed', 'null', ], ], 'mixedOrNever' => [ 'mixed', [ 'never', 'mixed', ], ], 'mixedOrObject' => [ 'mixed|object', [ 'mixed', 'object', ], ], 'mixedOrEmptyArray' => [ 'array|mixed', [ 'mixed', 'array', ], ], 'falseTrueToBool' => [ 'bool', [ 'false', 'true', ], ], 'trueFalseToBool' => [ 'bool', [ 'true', 'false', ], ], 'trueBoolToBool' => [ 'bool', [ 'true', 'bool', ], ], 'boolTrueToBool' => [ 'bool', [ 'bool', 'true', ], ], 'intOrTrueOrFalseToBool' => [ 'bool|int', [ 'int', 'false', 'true', ], ], 'intOrBoolOrTrueToBool' => [ 'bool|int', [ 'int', 'bool', 'true', ], ], 'intOrTrueOrBoolToBool' => [ 'bool|int', [ 'int', 'true', 'bool', ], ], 'arrayOfIntOrString' => [ 'array', [ 'array', 'array', ], ], 'arrayOfIntOrAlsoString' => [ 'array|string', [ 'array', 'string', ], ], 'emptyArrays' => [ 'array', [ 'array', 'array', ], ], 'arrayStringOrEmptyArray' => [ 'array', [ 'array', 'array', ], ], 'arrayMixedOrString' => [ 'array', [ 'array', 'array', ], ], 'arrayMixedOrStringKeys' => [ 'array', [ 'array', 'array', ], ], 'arrayMixedOrEmpty' => [ 'array', [ 'array', 'array', ], ], 'arrayBigCombination' => [ 'array', [ 'array', 'array', ], ], 'arrayTraversableToIterable' => [ 'iterable', [ 'array', 'Traversable', ], ], 'arrayIterableToIterable' => [ 'iterable', [ 'array', 'iterable', ], ], 'iterableArrayToIterable' => [ 'iterable', [ 'iterable', 'array', ], ], 'traversableIterableToIterable' => [ 'iterable', [ 'Traversable', 'iterable', ], ], 'iterableTraversableToIterable' => [ 'iterable', [ 'iterable', 'Traversable', ], ], 'arrayTraversableToIterableWithParams' => [ 'iterable', [ 'array', 'Traversable', ], ], 'arrayIterableToIterableWithParams' => [ 'iterable', [ 'array', 'iterable', ], ], 'iterableArrayToIterableWithParams' => [ 'iterable', [ 'iterable', 'array', ], ], 'traversableIterableToIterableWithParams' => [ 'iterable', [ 'Traversable', 'iterable', ], ], 'iterableTraversableToIterableWithParams' => [ 'iterable', [ 'iterable', 'Traversable', ], ], 'arrayObjectAndParamsWithEmptyArray' => [ 'ArrayObject|array', [ 'ArrayObject', 'array', ], ], 'emptyArrayWithArrayObjectAndParams' => [ 'ArrayObject|array', [ 'array', 'ArrayObject', ], ], 'falseDestruction' => [ 'bool', [ 'false', 'bool', ], ], 'onlyFalse' => [ 'false', [ 'false', ], ], 'onlyTrue' => [ 'true', [ 'true', ], ], 'falseFalseDestruction' => [ 'false', [ 'false', 'false', ], ], 'aAndAOfB' => [ 'A|A', [ 'A', 'A', ], ], 'combineObjectType1' => [ 'array{a?: int, b?: string}', [ 'array{a: int}', 'array{b: string}', ], ], 'combineObjectType2' => [ 'array{a: int|string, b?: string}', [ 'array{a: int}', 'array{a: string,b: string}', ], ], 'combineObjectTypeWithIntKeyedArray' => [ "array<'a'|int, int|string>", [ 'array{a: int}', 'array', ], ], 'combineNestedObjectTypeWithTKeyedArrayIntKeyedArray' => [ "array{a: array<'a'|int, int|string>}", [ 'array{a: array{a: int}}', 'array{a: array}', ], ], 'combineIntKeyedObjectTypeWithNestedIntKeyedArray' => [ "array>", [ 'array', 'array>', ], ], 'combineNestedObjectTypeWithNestedIntKeyedArray' => [ "array<'a'|int, array<'a'|int, int|string>>", [ 'array{a: array{a: int}}', 'array>', ], ], 'combinePossiblyUndefinedKeys' => [ 'array{a: bool, b?: mixed, d?: mixed}', [ 'array{a: false, b: mixed}', 'array{a: true, d: mixed}', 'array{a: true, d: mixed}', ], ], 'combinePossiblyUndefinedKeysAndString' => [ 'array{a: string, b?: int}|string', [ 'array{a: string, b?: int}', 'string', ], ], 'combineMixedArrayWithTKeyedArray' => [ 'array', [ 'array{a: int}', 'array', ], ], 'traversableAorB' => [ 'Traversable', [ 'Traversable', 'Traversable', ], ], 'iterableAorB' => [ 'iterable', [ 'iterable', 'iterable', ], ], 'FooAorB' => [ 'Foo|Foo', [ 'Foo', 'Foo', ], ], 'traversableOfMixed' => [ 'Traversable', [ 'Traversable', 'Traversable', ], ], 'traversableAndIterator' => [ 'Traversable&Iterator', [ 'Traversable&Iterator', 'Traversable&Iterator', ], ], 'traversableOfMixedAndIterator' => [ 'Traversable&Iterator', [ 'Traversable&Iterator', 'Traversable&Iterator', ], ], 'objectLikePlusArrayEqualsArray' => [ "array<'a'|'b'|'c', 1|2|3>", [ 'array<"a"|"b"|"c", 1|2|3>', 'array{a: 1|2, b: 2|3, c: 1|3}', ], ], 'combineClosures' => [ 'Closure(A):void|Closure(B):void', [ 'Closure(A):void', 'Closure(B):void', ], ], 'combineClassStringWithString' => [ 'string', [ 'class-string', 'string', ], ], 'combineClassStringWithFalse' => [ 'class-string|false', [ 'class-string', 'false', ], ], 'combineRefinedClassStringWithString' => [ 'string', [ 'class-string', 'string', ], ], 'combineRefinedClassStrings' => [ 'class-string|class-string', [ 'class-string', 'class-string', ], ], 'combineClassStringsWithLiteral' => [ 'class-string', [ 'class-string', 'Exception::class', ], ], 'combineClassStringWithNumericString' => [ 'class-string|numeric-string', [ 'class-string', 'numeric-string', ], ], 'combineRefinedClassStringWithNumericString' => [ 'class-string|numeric-string', [ 'class-string', 'numeric-string', ], ], 'combineClassStringWithTraitString' => [ 'class-string|trait-string', [ 'class-string', 'trait-string', ], ], 'combineRefinedClassStringWithTraitString' => [ 'class-string|trait-string', [ 'class-string', 'trait-string', ], ], 'combineCallableAndCallableString' => [ 'callable', [ 'callable', 'callable-string', ], ], 'combineCallableStringAndCallable' => [ 'callable', [ 'callable-string', 'callable', ], ], 'combineCallableAndCallableObject' => [ 'callable', [ 'callable', 'callable-object', ], ], 'combineCallableObjectAndCallable' => [ 'callable', [ 'callable-object', 'callable', ], ], 'combineCallableAndCallableArray' => [ 'callable', [ 'callable', 'callable-array', ], ], 'combineCallableArrayAndCallable' => [ 'callable', [ 'callable-array', 'callable', ], ], 'combineCallableArrayAndArray' => [ 'array', [ 'callable-array{class-string, string}', 'array', ], ], 'combineGenericArrayAndMixedArray' => [ 'array', [ 'array', 'array', ], ], 'combineTKeyedArrayAndArray' => [ 'array', [ 'array{hello: int}', 'array', ], ], 'combineTKeyedArrayAndNestedArray' => [ 'array', [ 'array{hello: array{goodbye: int}}', 'array', ], ], 'combineNumericStringWithLiteralString' => [ 'numeric-string', [ 'numeric-string', '"1"', ], ], 'combineLiteralStringWithNumericString' => [ 'numeric-string', [ '"1"', 'numeric-string', ], ], 'combineNonEmptyListWithTKeyedArrayList' => [ 'list{null|string, ...}', [ 'non-empty-list', 'array{null}', ], ], 'combineZeroAndPositiveInt' => [ 'int<0, max>', [ '0', 'positive-int', ], ], 'combinePositiveIntAndZero' => [ 'int<0, max>', [ 'positive-int', '0', ], ], 'combinePositiveIntAndMinusOne' => [ 'int<-1, max>', [ 'positive-int', '-1', ], ], 'combinePositiveIntZeroAndMinusOne' => [ 'int<-1, max>', [ '0', 'positive-int', '-1', ], ], 'combineMinusOneAndPositiveInt' => [ 'int<-1, max>', [ '-1', 'positive-int', ], ], 'combineZeroMinusOneAndPositiveInt' => [ 'int<-1, max>', [ '0', '-1', 'positive-int', ], ], 'combineZeroOneAndPositiveInt' => [ 'int<0, max>', [ '0', '1', 'positive-int', ], ], 'combinePositiveIntOneAndZero' => [ 'int<0, max>', [ 'positive-int', '1', '0', ], ], 'combinePositiveInts' => [ 'int<1, max>', [ 'positive-int', 'positive-int', ], ], 'combineNonEmptyArrayAndKeyedArray' => [ 'array', [ 'non-empty-array', 'array{0?:int}', ], ], 'combineNonEmptyStringAndLiteral' => [ 'non-empty-string', [ 'non-empty-string', '"foo"', ], ], 'combineLiteralAndNonEmptyString' => [ 'non-empty-string', [ '"foo"', 'non-empty-string', ], ], 'combineTruthyStringAndNonEmptyString' => [ 'non-empty-string', [ 'truthy-string', 'non-empty-string', ], ], 'combineNonFalsyNonEmptyString' => [ 'non-empty-string', [ 'non-falsy-string', 'non-empty-string', ], ], 'combineNonEmptyNonFalsyString' => [ 'non-empty-string', [ 'non-empty-string', 'non-falsy-string', ], ], 'combineNonEmptyStringAndNumericString' => [ 'non-empty-string', [ 'non-empty-string', 'numeric-string', ], ], 'combineNumericStringAndNonEmptyString' => [ 'non-empty-string', [ 'numeric-string', 'non-empty-string', ], ], 'combineNonEmptyLowercaseAndNonFalsyString' => [ 'non-empty-string', [ 'non-falsy-string', 'non-empty-lowercase-string', ], ], 'combineNonEmptyAndEmptyScalar' => [ 'scalar', [ 'non-empty-scalar', 'empty-scalar', ], ], 'combineLiteralStringAndNonspecificLiteral' => [ 'literal-string', [ 'literal-string', '"foo"', ], ], 'combineNonspecificLiteralAndLiteralString' => [ 'literal-string', [ '"foo"', 'literal-string', ], ], 'combineLiteralIntAndNonspecificLiteral' => [ 'literal-int', [ 'literal-int', '5', ], ], 'combineNonspecificLiteralAndLiteralInt' => [ 'literal-int', [ '5', 'literal-int', ], ], 'combineNonspecificLiteralAndPositiveInt' => [ 'int', [ 'positive-int', 'literal-int', ], ], 'combinePositiveAndLiteralInt' => [ 'int', [ 'literal-int', 'positive-int', ], ], 'combineNonEmptyStringAndNonEmptyNonSpecificLiteralString' => [ 'non-empty-string', [ 'non-empty-literal-string', 'non-empty-string', ], ], 'combineNonEmptyNonSpecificLiteralStringAndNonEmptyString' => [ 'non-empty-string', [ 'non-empty-string', 'non-empty-literal-string', ], ], ]; } private static function getAtomic(string $string): Atomic { return Type::parseString($string)->getSingleAtomic(); } }