diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 52826a1fbd3..d6f6e2eefb6 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -27,7 +27,6 @@ use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use PHPStan\Type\Traits\NonRemoveableTypeTrait; use function array_map; -use function array_merge; use function count; use function implode; use function in_array; @@ -94,47 +93,52 @@ public function inferTemplateTypesOn(Type $templateType): TemplateTypeMap return $types; } - /** - * @return string[] - */ public function getReferencedClasses(): array { $classes = []; foreach ($this->types as $type) { - $classes[] = $type->getReferencedClasses(); + foreach ($type->getReferencedClasses() as $className) { + $classes[] = $className; + } } - return array_merge(...$classes); + return $classes; } public function getArrays(): array { $arrays = []; foreach ($this->types as $type) { - $arrays[] = $type->getArrays(); + foreach ($type->getArrays() as $array) { + $arrays[] = $array; + } } - return array_merge(...$arrays); + return $arrays; } public function getConstantArrays(): array { $constantArrays = []; foreach ($this->types as $type) { - $constantArrays[] = $type->getConstantArrays(); + foreach ($type->getConstantArrays() as $constantArray) { + $constantArrays[] = $constantArray; + } } - return array_merge(...$constantArrays); + return $constantArrays; } public function getConstantStrings(): array { $strings = []; foreach ($this->types as $type) { - $strings[] = $type->getConstantStrings(); + foreach ($type->getConstantStrings() as $string) { + $strings[] = $string; + } } - return array_merge(...$strings); + return $strings; } public function accepts(Type $otherType, bool $strictTypes): TrinaryLogic diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index 1f54a943710..500214818c5 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -16,9 +16,7 @@ use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; -use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantBooleanType; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\Generic\GenericClassStringType; use PHPStan\Type\Generic\TemplateMixedType; use PHPStan\Type\Generic\TemplateType; @@ -27,7 +25,6 @@ use PHPStan\Type\Generic\TemplateUnionType; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use function array_map; -use function array_merge; use function count; use function implode; use function sprintf; @@ -80,24 +77,6 @@ public function getTypes(): array return $this->types; } - /** - * @param class-string $typeClass - * @return list - */ - private function getTypesOfClass(string $typeClass): array - { - $matchingTypes = []; - foreach ($this->getTypes() as $innerType) { - if (!$innerType instanceof $typeClass) { - return []; - } - - $matchingTypes[] = $innerType; - } - - return $matchingTypes; - } - public function isNormalized(): bool { return $this->normalized; @@ -125,52 +104,60 @@ public function getReferencedClasses(): array { $classes = []; foreach ($this->types as $type) { - $classes[] = $type->getReferencedClasses(); + foreach ($type->getReferencedClasses() as $className) { + $classes[] = $className; + } } - return array_merge(...$classes); + return $classes; } public function getArrays(): array { $arrays = []; foreach ($this->types as $type) { - $arrays[] = $type->getArrays(); - } - - if ($arrays === []) { - return []; + foreach ($type->getArrays() as $array) { + $arrays[] = $array; + } } - return array_merge(...$arrays); + return $arrays; } public function getConstantArrays(): array { $constantArrays = []; - foreach ($this->getTypesOfClass(ConstantArrayType::class) as $type) { - $constantArrays[] = $type->getConstantArrays(); - } + foreach ($this->types as $type) { + $typeAsConstantArrays = $type->getConstantArrays(); - if ($constantArrays === []) { - return []; + if ($typeAsConstantArrays === []) { + return []; + } + + foreach ($typeAsConstantArrays as $constantArray) { + $constantArrays[] = $constantArray; + } } - return array_merge(...$constantArrays); + return $constantArrays; } public function getConstantStrings(): array { $strings = []; - foreach ($this->getTypesOfClass(ConstantStringType::class) as $type) { - $strings[] = $type->getConstantStrings(); - } + foreach ($this->types as $type) { + $constantStrings = $type->getConstantStrings(); + + if ($constantStrings === []) { + return []; + } - if ($strings === []) { - return []; + foreach ($constantStrings as $string) { + $strings[] = $string; + } } - return array_merge(...$strings); + return $strings; } public function accepts(Type $type, bool $strictTypes): TrinaryLogic diff --git a/test.php b/test.php new file mode 100644 index 00000000000..3e51f3be13f --- /dev/null +++ b/test.php @@ -0,0 +1,23 @@ + $expectedDescriptions + */ + public function testGetConstantArrays( + array $types, + array $expectedDescriptions, + ): void + { + $unionType = TypeCombinator::union(...$types); + $constantArrays = $unionType->getConstantArrays(); + + $actualDescriptions = []; + foreach ($constantArrays as $constantArray) { + $actualDescriptions[] = $constantArray->describe(VerbosityLevel::precise()); + } + + $this->assertSame($expectedDescriptions, $actualDescriptions); + } + + public function dataGetConstantArrays(): iterable + { + yield from [ + [ + [ + TypeCombinator::intersect( + new ConstantArrayType( + [new ConstantIntegerType(1), new ConstantIntegerType(2)], + [new IntegerType(), new StringType()], + 2, + [0, 1], + ), + new NonEmptyArrayType(), + ), + new ConstantArrayType( + [new ConstantIntegerType(0), new ConstantIntegerType(1)], + [new ObjectType(Foo::class), new ObjectType(stdClass::class)], + 2, + ), + ], + [ + 'array{1?: int, 2?: string}', + 'array{RecursionCallable\Foo, stdClass}', + ], + ], + [ + [ + TypeCombinator::intersect( + new ConstantArrayType( + [new ConstantIntegerType(1), new ConstantIntegerType(2)], + [new IntegerType(), new StringType()], + 2, + [0, 1], + ), + ), + new IntegerType(), + ], + [], + ], + ]; + } + + /** + * @dataProvider dataGetConstantStrings + * @param list $expectedDescriptions + */ + public function testGetConstantStrings( + Type $unionType, + array $expectedDescriptions, + ): void + { + $constantStrings = $unionType->getConstantStrings(); + + $actualDescriptions = []; + foreach ($constantStrings as $constantString) { + $actualDescriptions[] = $constantString->describe(VerbosityLevel::precise()); + } + + $this->assertSame($expectedDescriptions, $actualDescriptions); + } + + public function dataGetConstantStrings(): iterable + { + yield from [ + [ + TypeCombinator::union( + new ConstantStringType('hello'), + new ConstantStringType('world'), + ), + [ + "'hello'", + "'world'", + ], + ], + [ + TypeCombinator::union( + new ConstantStringType(''), + TypeCombinator::intersect( + new StringType(), + new AccessoryNumericStringType(), + ), + ), + [], + ], + [ + new UnionType([ + new IntersectionType( + [ + new ConstantStringType('foo'), + new AccessoryLiteralStringType(), + ], + ), + new IntersectionType( + [ + new ConstantStringType('bar'), + new AccessoryLiteralStringType(), + ], + ), + ]), + [ + "'foo'", + "'bar'", + ], + ], + ]; + } + }