,error_levels?:string[]}> */ public function providerValidCodeParse(): iterable { return [ 'constantInFunction' => [ ' [ ' [ ' [ ' [ '$a' => 'int', '$b' => 'string', ], ], 'getClassConstantValue' => [ ' [ ' [], 'error_levels' => ['MixedArgument'], ], 'undefinedConstant' => [ ' [], 'error_levels' => ['UndefinedConstant'], ], 'suppressUndefinedClassConstant' => [ ' [], 'error_levels' => ['MixedAssignment'], ], 'hardToDefineClassConstant' => [ ' 4, "name" => 3 ]; const B = 4; } echo A::C[4];', ], 'sameNamedConstInOtherClass' => [ ' "one", ]; } echo A::C[4];', ], 'onlyMatchingConstantOffset' => [ ' 1, "two" => 2 ]; } foreach (A::KEYS as $key) { if (isset(A::ARR[$key])) { echo A::ARR[$key]; } }', ], 'stringArrayOffset' => [ ' 1, "b" => 2, ]; } function foo(string $s) : void { if (!isset(A::C[$s])) { return; } if ($s === "Hello") {} }', ], 'noExceptionsOnMixedArrayKey' => [ ' A::class, "type2" => B::class, ]; public function bar(array $data): void { if (!isset(self::TYPES[$data["type"]])) { throw new \InvalidArgumentException("Unknown type"); } $class = self::TYPES[$data["type"]]; $ret = finder($data["id"]); if (!$ret || !$ret instanceof $class) { throw new \InvalidArgumentException; } } }', 'assertions' => [], 'error_levels' => ['MixedArgument', 'MixedArrayOffset', 'MixedAssignment'], ], 'lateConstantResolution' => [ ' [ '$a' => 'string', '$b' => 'string', ], ], 'lateConstantResolutionParentArrayPlus' => [ ' true]; } class B extends A { public const ARR = parent::ARR + ["b" => true]; } class C extends B { public const ARR = parent::ARR + ["c" => true]; } /** @param array{a: true, b: true, c: true} $arg */ function foo(array $arg): void {} foo(C::ARR); ', ], 'lateConstantResolutionParentArraySpread' => [ ' [ ' [ ' $arg */ function foo(array $arg): void {} foo(C::ARR); ', ], 'classConstConcatEol' => [ ' ['$foo' => 'string'], ], 'allowConstCheckForDifferentPlatforms' => [ ' [ ' [ ' 1, B::class => 2, ]; /** * @param class-string $s */ function foo(string $s) : void { if (isset(C[$s])) {} }', ], 'resolveClassConstToCurrentClass' => [ ' [ ' [], 'error_levels' => ['MixedArgument'], ], 'arrayAccessAfterIsset' => [ ' ["c" => false], "c" => ["c" => true], "d" => ["c" => true] ]; } /** @var string */ $s = "b"; if (isset(C::A[$s]["c"]) && C::A[$s]["c"] === false) {}', ], 'namespacedConstantInsideClosure' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' */ function getMap(): array { return Mapper::MAP; } class Mapper { public const MAP = [ Foo::class => self::A, Foo::BAR => self::A, ]; private const A = 5; } class Foo { public const BAR = "bar"; }' ], 'resolveConstArrayAsList' => [ ' $value */ function test($value): void { print_r($value); } test(Test1::VALUES); test(Test2::VALUES);' ], 'resolveConstantFetchViaFunction' => [ ' [ ' null, "A01" => null, "A02" => null, "A03" => null, "A04" => null, "A05" => null, "A06" => null, "A07" => null, "A010" => null, "A011" => null, "A012" => null, "A013" => null, "A014" => null, "A015" => null, "A016" => null, "A017" => null, "A020" => null, "A021" => null, "A022" => null, "A023" => null, "A024" => null, "A025" => null, "A026" => null, "A027" => null, "A030" => null, "A031" => null, "A032" => null, "A033" => null, "A034" => null, "A035" => null, "A036" => null, "A037" => null, "A040" => null, "A041" => null, "A042" => null, "A043" => null, "A044" => null, "A045" => null, "A046" => null, "A047" => null, "A050" => null, "A051" => null, "A052" => null, "A053" => null, "A054" => null, "A055" => null, "A056" => null, "A057" => null, "A060" => null, "A061" => null, "A062" => null, "A063" => null, "A064" => self::SUCCEED, "A065" => self::FAIL, ]; const SUCCEED = "SUCCEED"; const FAIL = "FAIL"; /** * @param string $code */ public static function will_succeed($code) : bool { // False positive TypeDoesNotContainType - string(SUCCEED) cannot be identical to null // This seems to happen because the array has a lot of entries. return (self::LOOKUP[strtoupper($code)] ?? null) === self::SUCCEED; } }' ], 'keyOf' => [ ' "a", 2 => "b", 3 => "c" ]; /** * @param key-of $i */ public static function foo(int $i) : void {} } A::foo(1); A::foo(2); A::foo(3);', ], 'valueOf' => [ ' "a", 2 => "b", 3 => "c" ]; /** * @param value-of $j */ public static function bar(string $j) : void {} } A::bar("a"); A::bar("b"); A::bar("c");', ], 'valueOfDefault' => [ ' "a", 2 => "b", 3 => "c" ]; /** * @var value-of */ public $foo = "a"; }', ], 'wildcardEnum' => [ ' [ ' [ ' */ class A implements AInterface { const C_1 = 1; const C_2 = 2; const C_3 = 3; const D_4 = 4; public function foo($i) { return $i; } } $a = new A(); $a->foo(1); $a->foo(2); $a->foo(3); $a->foo(A::D_4);', ], 'wildcardVarAndReturn' => [ 'number = $number; } /** * @return Numbers::* */ public function get(): int { return $this->number; } }' ], 'lowercaseStringAccessClassConstant' => [ ' 1, "b" => 2, "c" => 3 ]; } /** * @param lowercase-string $s */ function foo(string $s, string $t) : void { echo A::C[$t]; echo A::C[$s]; }' ], 'getClassConstantOffset' => [ ' "string" ]; private const B = self::A[0]; public function foo(): string { return self::B; } }' ], 'bitwiseOrClassConstant' => [ ' 'int', ] ], 'protectedClassConstantAccessibilitySameNameInChild' => [ ' [ ' self::KEYS[\'hi\']]; public const KEYS = [\'hi\' => CONSTANTS::THERE]; } class CONSTANTS { public const THERE = \'there\'; } echo B::VALUES["there"];' ], 'internalConstWildcard' => [ ' [ 'level = $level; } /** * @psalm-return self */ public static function readUncommitted(): self { return new self(self::READ_UNCOMMITTED); } /** * @psalm-return self */ public static function readCommitted(): self { return new self(self::READ_COMMITTED); } /** * @psalm-return self */ public static function repeatableRead(): self { return new self(self::REPEATABLE_READ); } /** * @psalm-return self */ public static function serializable(): self { return new self(self::SERIALIZABLE); } /** * @psalm-return T */ public function toString(): string { return $this->level; } }' ], 'dirAndFileInConstInitializersAreNonEmptyString' => [ ' 'non-empty-string', '$file===' => 'non-empty-string', ] ], 'lineInConstInitializersIsInt' => [ ' 'int', ] ], 'classMethodTraitAndFunctionInConstInitializersAreStrings' => [ ' 'string', '$mtd' => 'string', '$trt' => 'string', '$fcn' => 'string', ] ], 'concatWithMagicInConstInitializersIsNoEmptyString' => [ ' 'non-empty-string', '$file===' => 'non-empty-string', ] ], 'noCrashWithStaticInDocblock' => [ ' [ ' [ ' [ '$arr===' => 'array{1, 2}', ], ], 'keysInUnpackedArrayAreReset' => [ ' 2]]; } $arr = C::A; ', 'assertions' => [ '$arr===' => 'array{2}', ], ], 'arrayKeysSequenceContinuesAfterExplicitIntKey' => [ ' "a", "z", 10 => "aa", "zz"]; } $arr = C::A; ', 'assertions' => [ '$arr===' => 'array{10: "aa", 11: "zz", 5: "a", 6: "z"}', ], ], 'arrayKeysSequenceContinuesAfterNonIntKey' => [ ' "a", "zz" => "z", "aa"]; } $arr = C::A; ', 'assertions' => [ '$arr===' => 'array{5: "a", 6: "aa", zz: "z"}', ], ], ]; } /** * @return iterable */ public function providerInvalidCodeParse(): iterable { return [ 'constantDefinedInFunctionButNotCalled' => [ ' 'UndefinedConstant', ], 'undefinedClassConstantInParamDefault' => [ ' 'UndefinedConstant', ], 'nonMatchingConstantOffset' => [ ' 1, "two" => 2 ]; const ARR2 = [ "three" => 3, "four" => 4 ]; } foreach (A::KEYS as $key) { if (isset(A::ARR[$key])) { echo A::ARR2[$key]; } }', 'error_message' => 'InvalidArrayOffset', ], 'objectLikeConstArrays' => [ ' "zero", self::B => "two", ]; } if (C::ARR[C::A] === "two") {}', 'error_message' => 'TypeDoesNotContainType', ], 'missingClassConstInArray' => [ ' 'UndefinedConstant', ], 'resolveConstToCurrentClassWithBadReturn' => [ ' 'InvalidReturnStatement', ], 'outOfScopeDefinedConstant' => [ ' 'UndefinedConstant', ], 'preventStaticClassConstWithoutRef' => [ ' 'UndefinedConstant', ], 'noCyclicConstReferences' => [ ' 'CircularReference' ], 'keyOfBadValue' => [ ' "a", 2 => "b", 3 => "c" ]; /** * @param key-of $i */ public static function foo(int $i) : void {} } A::foo(4);', 'error_message' => 'InvalidArgument', ], 'valueOfBadValue' => [ ' "a", 2 => "b", 3 => "c" ]; /** * @param value-of $j */ public static function bar(string $j) : void {} } A::bar("d");', 'error_message' => 'InvalidArgument', ], 'wildcardEnumBadValue' => [ ' 'InvalidArgument' ], 'wildcardEnumAnyTemplateExtendConstantBadValue' => [ ' */ class A implements AInterface { const C_1 = 1; const C_2 = 2; const C_3 = 3; const D_4 = 4; public function foo($i) { return $i; } } $a = new A(); $a->foo(5); ', 'error_message' => 'InvalidArgument' ], 'correctMessage' => [ ' "a", 2 => "b"][$s]; }', 'error_message' => "offset value of '1|0" ], 'constantWithMissingClass' => [ ' 'UndefinedClass', ], ]; } }