[ 'code' => ' */ public function getKey() { return "bar"; } } ', ], 'valueOfAssociativeArrayClassConstant' => [ 'code' => ' 42 ]; /** @return value-of */ public function getValue() { return 42; } } ', ], 'allValuesOfAssociativeArrayPossible' => [ 'code' => ' 42, "adams" => 43, ]; /** @return value-of */ public function getValue(bool $adams) { if ($adams) { return 42; } return 43; } } ', ], 'valueOfAsArray' => [ 'code' => ' 42, "adams" => 43, ]; /** @return value-of[] */ public function getValues() { return array_values(self::FOO); } } ', ], 'valueOfArrayLiteral' => [ 'code' => '> */ function getKey() { return "42"; } ', ], 'valueOfUnionArrayLiteral' => [ 'code' => '|array> */ function getValue(bool $asFloat) { if ($asFloat) { return 42.0; } return 42; } ', ], 'valueOfStringArrayConformsToString' => [ 'code' => '>[] */ $keys2 = ["foo"]; return $keys2[0]; } ', ], 'acceptLiteralIntInValueOfUnionLiteralInts' => [ 'code' => '|array{0: 3, 1: 4}> */ function getValue(int $i) { if ($i >= 0 && $i <= 4) { return $i; } return 0; } ', ], 'valueOfExpandsPropertiesOf' => [ 'code' => '>> */ function returnPropertyOfA() { return [true, "bar", 42]; } ', ], 'valueOfStringEnum' => [ 'code' => ' $arg */ function foobar(string $arg): void { /** @psalm-check-type-exact $arg = "foo"|"bar" */; } /** @var Foo */ $foo = Foo::Foo; foobar($foo->value); ', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '8.1', ], 'valueOfIntEnum' => [ 'code' => ' $arg */ function foobar(int $arg): void { /** @psalm-check-type-exact $arg = 2|3 */; } /** @var Foo */ $foo = Foo::Foo; foobar($foo->value); ', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '8.1', ], 'valueOfEnumUnion' => [ 'code' => ' $arg */ function foobar(int|string $arg): void { /** @psalm-check-type-exact $arg = 2|3|"foo"|"bar" */; } ', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '8.1', ], 'valueOfTemplatedEnum' => [ 'code' => ' */ public static function values(): array { $cases = self::cases(); return array_map( static fn (BackedEnum $enum) => $enum->value, $cases ); } } enum Bar: string { /** * @use ValuesFromEnumTrait> */ use ValuesFromEnumTrait; case BAZ = "baz"; } $values = Bar::values(); ', 'assertions' => [ '$values===' => 'list<\'baz\'>', ], 'ignored_issues' => [], 'php_version' => '8.1', ], ]; } public function providerInvalidCodeParse(): iterable { return [ 'onlyDefinedValuesOfConstantList' => [ 'code' => ' */ public function getValue() { return "adams"; } } ', 'error_message' => 'InvalidReturnStatement', ], 'noIntForValueOfStringArrayLiteral' => [ 'code' => '> */ public function getValue() { return 42; } } ', 'error_message' => 'InvalidReturnStatement', ], 'noStringForValueOfIntList' => [ 'code' => '> */ public function getValue() { return "42"; } } ', 'error_message' => 'InvalidReturnStatement', ], 'noOtherStringAllowedForValueOfKeyedArray' => [ 'code' => ' */ function getValue() { return "adams"; } ', 'error_message' => 'InvalidReturnStatement', ], 'noOtherIntAllowedInValueOfUnionLiteralInts' => [ 'code' => '|array{0: 3, 1: 4}> */ function getValue() { return 5; } ', 'error_message' => 'InvalidReturnStatement', ], 'valueOfUnitEnum' => [ 'code' => ' $arg */ function foobar(string $arg): void {} ', // TODO turn this into an InvalidDocblock with a better error message. This is difficult because it // has to happen after scanning has finished, otherwise the class might not have been scanned yet. 'error_message' => 'MismatchingDocblockParamType', 'ignored_issues' => [], 'php_version' => '8.1', ], ]; } }