,error_levels?:string[]}> */ public function providerValidCodeParse(): iterable { return [ 'allowBoundedType' => [ ' */ function returnFooBase() : Foo { $f = new Foo(function () { return new Child(); }); return $f; }', ], 'allowMoreSpecificArray' => [ ' */ private $FooArray; public function __construct() { $this->FooArray = new Foo(function(): array { return ["foo" => "bar"]; }); } }' ], 'specializeTypeInPropertyAssignment' => [ 'closure = $closure; } } class Bar { /** @var Foo */ private $FooArray; public function __construct() { $this->FooArray = new Foo(function(): array { return ["foo" => "bar"]; }); expectsShape($this->FooArray); } } /** @param Foo $_ */ function expectsShape($_): void {}', ], 'allowPassingToCovariantCollectionWithoutExtends' => [ ' */ class Collection implements IteratorAggregate { private $arr; /** * @param array $arr */ public function __construct(array $arr) { $this->arr = $arr; } /** @return Traversable */ public function getIterator() { foreach ($this->arr as $k => $v) { yield $k => $v; } } } /** * @param Collection $list */ function getSounds(Collection $list) : void { foreach ($list as $l) { $l->getSound(); } } /** * @param Collection $list */ function takesDogList(Collection $list) : void { getSounds($list); // this probably should not be an error }', ], 'allowPassingToCovariantCollectionWithExtends' => [ ' */ class Collection implements IteratorAggregate { private $arr; /** * @param array $arr */ public function __construct(array $arr) { $this->arr = $arr; } /** @return Traversable */ public function getIterator() { foreach ($this->arr as $k => $v) { yield $k => $v; } } } /** @template-extends Collection */ class HardwiredDogCollection extends Collection {} /** * @param Collection $list */ function getSounds(Collection $list) : void { foreach ($list as $l) { echo $l->getSound(); } } getSounds(new HardwiredDogCollection([new Dog]));', ], 'butWithCatInstead' => [ 'name = $name; } } /** @implements Viewable */ class Cat implements Viewable { public function view(): object { return new CatView("Kittie"); } } /** @psalm-param Viewable $viewable */ function getView(Viewable $viewable): object { return $viewable->view(); } getView(new Cat());' ], 'allowExtendingInterfaceWithExtraParam' => [ ' */ class Collection implements CollectionInterface { /** @param list $elements */ public function __construct(array $elements) {} } class Element implements ElementInterface {} /** @param CollectionInterface $col */ function usesElementInterfaceCollection(CollectionInterface $col) :void {}' ], 'extendsCovariantCoreClassWithSameParamCount' => [ ' */ class MyArray implements IteratorAggregate { /** @var array */ private $values = []; public function __construct() { $this->values = []; } public function getIterator() : Traversable { return new ArrayObject($this->values); } } class A {} class AChild extends A {} /** @param IteratorAggregate $i */ function takesIteratorAggregate(IteratorAggregate $i) : void {} /** @param MyArray $a */ function takesMyArrayOfException(MyArray $a) : void { takesIteratorAggregate($a); }' ], 'extendsCovariantCoreClassWithSubstitutedParam' => [ ' */ class MyArray implements IteratorAggregate { /** @var array */ private $values = []; public function __construct() { $this->values = []; } public function getIterator() : Traversable { return new ArrayObject($this->values); } } class A {} class AChild extends A {} /** @param IteratorAggregate $i */ function takesIteratorAggregate(IteratorAggregate $i) : void {} /** @param MyArray $a */ function takesMyArrayOfException(MyArray $a) : void { takesIteratorAggregate($a); }' ], 'allowImmutableCovariance' => [ ' */ private $arr = []; /** * @param T ...$a */ public function __construct(...$a) { $this->arr = $a; } /** * @param T $a * @return Collection */ public function add($a) : Collection { return new Collection(...$this->arr, $a); } } /** * @template T * @param Collection $c * @return Collection */ function covariant(Collection $c) : Collection { return $c->add(new Cat()); } $dogs = new Collection(new Dog(), new Dog()); $cats = new Collection(new Cat(), new Cat()); $misc = new Collection(new Cat(), new Dog()); covariant($dogs); covariant($cats); covariant($misc);', ], 'allowCovariantReferenceToMapToCovariant' => [ 'value = $value; } /** @return T */ public function get() { return $this->value; } } /** * @template-covariant T */ class C { /** @var CovariantReference */ private $reference; /** @param CovariantReference $reference */ public function __construct($reference) { $this->reference = $reference; } /** @return CovariantReference */ function getReference() { return $this->reference; } }' ], 'allowCovariantReturnOnArrays' => [ ' $arr */ public function __construct(array $arr) { $this->arr = $arr; } /** @psalm-return array */ public function foo(): array { return $this->arr; } }', ], 'allowIteratorCovariance' => [ ' */ public function foo(): Traversable; } /** * @template-covariant T */ interface IArray { /** @psalm-return array */ public function foo(): array; } /** * @template-covariant T */ interface IIterable { /** @psalm-return iterable */ public function foo(): iterable; }', ], 'extendWithArrayTemplate' => [ '> */ public function zip(); } /** * @template T2 * @extends C */ interface AC extends C { /** * @psalm-return self> */ public function zip(); }', ], 'extendsArrayWithCovariant' => [ '> */ public function getNested(): IParentCollection; } /** * @template T2 * * @extends IParentCollection */ interface IChildCollection extends IParentCollection { /** * @return IChildCollection> */ public function getNested(): IChildCollection; }', [], [], '7.4', ], 'extendsTKeyedArrayWithCovariant' => [ ' */ public function getNested(): IParentCollection; } /** * @template T2 * * @extends IParentCollection */ interface IChildCollection extends IParentCollection { /** * @return IChildCollection */ public function getNested(): IChildCollection; }', [], [], '7.4', ], 'noNeedForCovarianceWithFunctionTemplate' => [ 'value = $value; } } /** * @template T of SomeParent * @param Container $container */ function run(Container $container): void{} run(new Container(new TypeA()));' ], ]; } /** * @return iterable */ public function providerInvalidCodeParse(): iterable { return [ 'preventCovariantParamUsage' => [ ' 'InvalidTemplateParam', ], 'preventExtendingWithCovariance' => [ ' */ class CovariantFoo extends InvariantFoo {}', 'error_message' => 'InvalidTemplateParam', ], 'expectsTemplatedObject' => [ ' */ class MyArray implements ArrayAccess, IteratorAggregate { /** @var array */ private $values = []; public function __construct() { $this->values = []; } /** * @param int $offset * @param T $value */ public function offsetSet($offset, $value) { $this->values[$offset] = $value; } /** * @param int $offset * @return T */ public function offsetGet($offset) { return $this->values[$offset]; } /** * @param int $offset * @return bool */ public function offsetExists($offset) { return isset($this->values[$offset]); } /** * @param int $offset */ public function offsetUnset($offset) { unset($this->values[$offset]); } public function getIterator() : Traversable { return new ArrayObject($this->values); } } class A {} class AChild extends A {} /** @param IteratorAggregate $i */ function expectsIteratorAggregateOfA(IteratorAggregate $i) : void {} /** @param MyArray $m */ function takesMyArrayOfAChild(MyArray $m) : void { expectsIteratorAggregateOfA($m); }', 'error_message' => 'MixedArgumentTypeCoercion', ], 'preventGeneratorVariance' => [ ' */ function gen() : Generator { $bar = yield new Bar(); $bar->b(); } /** @param Generator $gen */ function sendFoo(Generator $gen): void { $gen->send(new Foo()); } $gen = gen(); sendFoo($gen);', 'error_message' => 'InvalidArgument', ], 'preventCovariantParamMappingToInvariant' => [ 'value = $value; } /** @return T */ public function get() { return $this->value; } } /** * @template-covariant T */ class C { /** @var InvariantReference */ private InvariantReference $reference; /** @param InvariantReference $reference */ public function __construct(InvariantReference $reference) { $this->reference = $reference; } /** @return InvariantReference */ public function getReference() : InvariantReference { return $this->reference; } }', 'error_message' => 'InvalidTemplateParam' ], 'preventExtendingCoreWithCovariantParam' => [ ' */ class Collection extends \ArrayObject {}', 'error_message' => 'InvalidTemplateParam', ], ]; } }