,error_levels?:string[]}> */ public function providerValidCodeParse() { 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 {}', ], 'allowPassingToCovariantCollection' => [ ' */ class Collection extends \ArrayObject { /** * @param array $kv */ public function __construct(array $kv) { parent::__construct($kv); } } /** * @param Collection $list */ function getSounds(Traversable $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 extends \ArrayObject {} /** @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);', ], ]; } /** * @return iterable */ public function providerInvalidCodeParse() { 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() { $bar = yield new Bar(); $bar->b(); } /** @param Generator $gen */ function sendFoo($gen): void { $gen->send(new Foo()); } $gen = gen(); sendFoo($gen);', 'error_message' => 'InvalidArgument', ], ]; } }