,error_levels?:string[]}> */ public function providerValidCodeParse() { return [ 'assertTemplatedType' => [ 'sayHello();', ], 'assertInstanceofTemplatedClassMethodUnknownClass' => [ ' $expected * @param mixed $actual * @psalm-assert T $actual */ public function assertInstanceOf($expected, $actual) : void {} /** * @param class-string $c */ function bar(string $c, object $e) : void { $this->assertInstanceOf($c, $e); echo $e->getCode(); } }', [], ['MixedArgument', 'MixedMethodCall'], ], 'assertInstanceofTemplatedClassMethodUnknownStringClass' => [ ' $expected * @param mixed $actual * @psalm-assert T $actual */ public function assertInstanceOf($expected, $actual) : void {} function bar(string $c, object $e) : void { $this->assertInstanceOf($c, $e); echo $e->getCode(); } }', [], ['MixedArgument', 'MixedMethodCall', 'ArgumentTypeCoercion'], ], 'assertInstanceofTemplatedFunctionUnknownClass' => [ ' $expected * @param mixed $actual * @psalm-assert T $actual */ function assertInstanceOf($expected, $actual) : void {} /** * @param class-string $c */ function bar(string $c, object $e) : void { assertInstanceOf($c, $e); echo $e->getCode(); }', [], ['MixedArgument', 'MixedMethodCall'], ], 'assertInstanceofTemplatedFunctionUnknownStringClass' => [ ' $expected * @param mixed $actual * @psalm-assert T $actual */ function assertInstanceOf($expected, $actual) : void {} function bar(string $c, object $e) : void { assertInstanceOf($c, $e); echo $e->getCode(); }', [], ['MixedArgument', 'MixedMethodCall', 'ArgumentTypeCoercion'], ], 'assertTypedArray' => [ ' $expected * @param mixed $actual * @psalm-assert T[] $actual */ function assertArrayOf($expected, $actual) : void {} function bar(array $arr) : void { assertArrayOf(A::class, $arr); foreach ($arr as $a) { $a->foo(); } }', ], 'assertTemplatedTypeString' => [ 'bat();', [ '$f' => 'Foo', ], ], 'suppressRedundantCondition' => [ ' [ ' [ ' [ ' [ ' [ ' $i * * @param iterable $i * @param class-string $type */ function assertAllInstanceOf(iterable $i, string $type): void { /** @psalm-suppress MixedAssignment */ foreach ($i as $elt) { if (!$elt instanceof $type) { throw new \UnexpectedValueException(""); } } } class A {} function getArray(): array { return []; } $array = getArray(); assertAllInstanceOf($array, A::class);', [ '$array' => 'array', ], ], 'assertAllIterableOfClass' => [ ' $i * * @param iterable $i * @param class-string $type */ function assertAllInstanceOf(iterable $i, string $type): void { /** @psalm-suppress MixedAssignment */ foreach ($i as $elt) { if (!$elt instanceof $type) { throw new \UnexpectedValueException(""); } } } class A {} function getIterable(): iterable { return []; } $iterable = getIterable(); assertAllInstanceOf($iterable, A::class);', [ '$iterable' => 'iterable', ], ], 'complicatedAssertAllInstanceOf' => [ ' $i * * @param iterable $i * @param class-string $type */ function allInstanceOf(iterable $i, string $type): bool { /** @psalm-suppress MixedAssignment */ foreach ($i as $elt) { if (!$elt instanceof $type) { return false; } } return true; } interface IBlogPost { public function getId(): int; } function getData(): iterable { return []; } $data = getData(); assert(allInstanceOf($data, IBlogPost::class)); foreach ($data as $post) { echo $post->getId(); }', ], 'assertUnionInNamespace' => [ ' $interface * @psalm-assert ExpectedType|class-string $value */ function implementsInterface($value, $interface, string $message = ""): void {} /** * @psalm-template ExpectedType of object * @param mixed $value * @psalm-param class-string $interface * @psalm-assert null|ExpectedType|class-string $value */ function nullOrImplementsInterface(?object $value, $interface, string $message = ""): void {} interface A { } /** * @param mixed $value * * @psalm-return A|class-string */ function consume($value) { implementsInterface($value, A::class); return $value; } /** * @param mixed $value * * @psalm-return A|class-string|null */ function consume2($value) { nullOrImplementsInterface($value, A::class); return $value; }' ], 'assertTemplatedTemplateSimple' => [ ' $c * * @return T2 */ function example(Clazz $c) { /** @var mixed */ $x = 0; $c->is($x); return $x; }' ], 'assertTemplatedTemplateIfTrue' => [ ' $c * * @return T2|false */ function example(Clazz $c) { /** @var mixed */ $x = 0; return $c->is($x) ? $x : false; }' ], 'assertOnClass' => [ 'matches($value)); return $value; } }' ], 'noCrashWhenAsserting' => [ ' $expectedType * @psalm-assert class-string $actualType */ function assertIsA(string $expectedType, string $actualType): void { \assert(\is_a($actualType, $expectedType, true)); } class Foo { /** * @psalm-template OriginalClass of object * @psalm-param class-string $originalClass * @psalm-return class-string|null */ private function generateProxy(string $originalClass) : ?string { $generatedClassName = self::class . \'\\\\\' . $originalClass; if (class_exists($generatedClassName)) { assertIsA($originalClass, $generatedClassName); return $generatedClassName; } return null; } }', ], 'castClassStringWithIsA' => [ ' $expectedType * @psalm-return class-string */ function castStringToClassString(string $expectedType, string $anyString): string { \assert(\is_a($anyString, $expectedType, true)); return $anyString; }' ], 'classTemplateAssert' => [ 'value = $value; } } /** * @template FieldDefinitionType * * @param string|bool|int|null $value * @param FieldDefinition $definition * * @return FieldValue */ function fromScalarAndDefinition($value, FieldDefinition $definition) : FieldValue { $definition->assertAppliesToValue($value); return new FieldValue($value); } /** * @template ExpectedFieldType */ final class FieldDefinition { /** * @param mixed $value * @psalm-assert ExpectedFieldType $value */ public function assertAppliesToValue($value): void { throw new \Exception("bad"); } }' ], 'assertThrowsInstanceOfFunction' => [ ' $exceptionType * @psalm-assert T $outerEx */ function assertThrowsInstanceOf(\Throwable $outerEx, string $exceptionType) : void { if (!($outerEx instanceof $exceptionType)) { throw new \Exception("thrown instance of wrong type"); } }' ], ]; } /** * @return iterable */ public function providerInvalidCodeParse() { return [ 'detectRedundantCondition' => [ ' 'RedundantCondition', ], 'detectAssertSameTypeDoesNotContainType' => [ ' 'TypeDoesNotContainType', ], 'detectAssertAlwaysSame' => [ ' 'RedundantCondition', ], 'detectNeverCanBeSameAfterAssertion' => [ ' 'TypeDoesNotContainType', ], 'detectNeverCanBeNotSameAfterAssertion' => [ ' 'RedundantCondition', ], 'detectNeverCanBeEqualAfterAssertion' => [ ' 'TypeDoesNotContainType', ], 'detectIntFloatNeverCanBeEqualAfterAssertion' => [ ' 'TypeDoesNotContainType', ], 'detectFloatIntNeverCanBeEqualAfterAssertion' => [ ' 'TypeDoesNotContainType', ], 'assertNotSameDifferentTypes' => [ ' 'RedundantCondition', ], 'assertNotSameDifferentTypesExplicitString' => [ ' 'RedundantCondition', ], ]; } }