[ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' */ class Bar { public function foo() : void { $bar = /** @return TA */ function() { return ["hello"]; }; /** @var array */ $bat = [$bar(), $bar()]; foreach ($bat as $b) { echo $b[0]; } } } /** * @psalm-type _A=array{elt:int} * @param _A $p * @return _A */ function f($p) { /** @var _A */ $r = $p; return $r; }', ], 'classTypeAliasSimple' => [ 'code' => ' "Nokia"]; } } /** @psalm-type NameType = array{name: string} */ class Name { /** @psalm-return NameType */ function toArray(): array { return ["name" => "Matt"]; } } /** * @psalm-import-type PhoneType from Phone as PhoneType2 * @psalm-import-type NameType from Name as NameType2 * * @psalm-type UserType = PhoneType2&NameType2 */ class User { /** @psalm-return UserType */ function toArray(): array { return array_merge( (new Name)->toArray(), (new Phone)->toArray() ); } }' ], 'classTypeAliasImportWithAlias' => [ 'code' => ' "Nokia"]; } } /** * @psalm-import-type PhoneType from Phone as TPhone */ class User { /** @psalm-return TPhone */ function toArray(): array { return array_merge([], (new Phone)->toArray()); } }' ], 'classTypeAliasDirectUsage' => [ 'code' => ' "Nokia"]; } } /** * @psalm-import-type PhoneType from Phone */ class User { /** @psalm-return PhoneType */ function toArray(): array { return array_merge([], (new Phone)->toArray()); } }' ], 'classTypeAliasFromExternalNamespace' => [ 'code' => ' "Nokia"]; } } } namespace Bar { /** * @psalm-import-type PhoneType from \Foo\Phone */ class User { /** @psalm-return PhoneType */ function toArray(): array { return (new \Foo\Phone)->toArray(); } } }' ], 'importTypeForParam' => [ 'code' => 'b($type); } /** * @psalm-param Type2 $type */ private function b(int $type): void { } }' ], 'usedInVarForForeach' => [ 'code' => ' [ 'code' => ' [ 'code' => ' */ class C implements A { public function output() { return "hello"; } } $instance = new C(); $output = $instance->output();', 'assertions' => [ '$output' => 'string', ], ], 'sameDocBlockTypeAliasAsTypeParameterForExtendedRegularClass' => [ 'code' => 'value = $value; } } /** * @psalm-type Foo=string * @extends A */ class C extends A {} $instance = new C("hello"); $output = $instance->value;', 'assertions' => [ '$output' => 'string', ], ], 'sameDocBlockTypeAliasAsTypeParameterForExtendedAbstractClass' => [ 'code' => 'value = $value; } } /** * @psalm-type Foo=string * @extends A */ class C extends A {} $instance = new C("hello"); $output = $instance->value;', 'assertions' => [ '$output' => 'string', ], ], 'importedTypeAliasAsTypeParameterForImplementation' => [ 'code' => ' */ class C implements A {}', ], 'importedTypeAliasAsConstrainedTypeParameterForImplementation' => [ 'code' => ' */ class C implements A {} ' ], 'importedTypeAliasAsTypeParameterForExtendedClass' => [ 'code' => ' */ class C extends A {}', ], 'importedTypeAliasAsTypeParameterForExtendedAbstractClass' => [ 'code' => ' */ class C extends A {}', ], 'importedTypeAliasRenamedAsTypeParameterForImplementation' => [ 'code' => ' */ class C implements A {}', ], 'importedTypeAliasRenamedAsTypeParameterForExtendedClass' => [ 'code' => ' */ class C extends A {}', ], 'importedTypeAliasRenamedAsTypeParameterForExtendedAbstractClass' => [ 'code' => ' */ class C extends A {}', ], 'importedTypeInsideLocalTypeAliasUsedAsTypeParameter' => [ 'code' => 'value = $value; } } /** * @psalm-type Foo=string */ class B {} /** * @psalm-import-type Foo from B * @psalm-type Baz=Foo * * @extends A */ class C extends A {} $instance = new C("hello"); $output = $instance->value;', 'assertions' => [ '$output' => 'string', ], ], 'importedTypeWithPhpstanAnnotation' => [ 'code' => 'value = $value; } } /** * @phpstan-type Foo=string */ class B {} /** * @phpstan-import-type Foo from B * @phpstan-type Baz=Foo * * @extends A */ class C extends A {} $instance = new C("hello"); $output = $instance->value;', 'assertions' => [ '$output' => 'string', ], ], 'importedTypeUsedInAssertion' => [ 'code' => 'assertFoo($input); return $input; } /** * @param mixed $value * @psalm-assert FooAlias $value */ private function assertFoo($value): void { if(!is_string($value)) { throw new \InvalidArgumentException(); } } } $instance = new B(); $output = $instance->convertToFoo("hallo"); ', 'assertions' => [ '$output' => 'string', ] ], 'importedTypeUsedInOtherType' => [ 'code' => ' */ class Main { /** @return OpeningTypeAssignment */ public function doStuff(): array { return []; } } $instance = new Main(); $output = $instance->doStuff(); ', 'assertions' => [ '$output===' => 'list<1|2>', ] ] ]; } public function providerInvalidCodeParse(): iterable { return [ 'invalidTypeAlias' => [ 'code' => ' */ class A {}', 'error_message' => 'InvalidDocblock', ], 'typeAliasInTKeyedArray' => [ 'code' => ' 'InvalidReturnStatement', ], 'classTypeAliasInvalidReturn' => [ 'code' => ' "Nokia"]; } } /** @psalm-type NameType = array{name: string} */ class Name { /** @psalm-return NameType */ function toArray(): array { return ["name" => "Matt"]; } } /** * @psalm-import-type PhoneType from Phone as PhoneType2 * @psalm-import-type NameType from Name as NameType2 * * @psalm-type UserType = PhoneType2&NameType2 */ class User { /** @psalm-return UserType */ function toArray(): array { return array_merge( (new Name)->toArray(), ["foo" => "bar"] ); } }', 'error_message' => 'InvalidReturnStatement', ], 'classTypeInvalidAliasImport' => [ 'code' => ' "Matt"]; } } /** * @psalm-import-type PhoneType from Phone */ class User {}', 'error_message' => 'InvalidTypeImport', ], 'classTypeAliasFromInvalidClass' => [ 'code' => ' 'UndefinedDocblockClass', ], 'malformedImportMissingFrom' => [ 'code' => ' 'InvalidTypeImport', ], 'malformedImportMissingSourceClass' => [ 'code' => ' 'InvalidTypeImport', ], 'malformedImportMisspelledFrom' => [ 'code' => ' 'InvalidTypeImport', ], 'malformedImportMissingAlias' => [ 'code' => ' 'InvalidTypeImport', ], 'noCrashWithPriorReference' => [ 'code' => ' 'UndefinedDocblockClass', ], 'mergeImportedTypes' => [ 'code' => ' 'PossiblyUndefinedArrayOffset', ], 'noCrashWithSelfReferencingType' => [ 'code' => ' 'InvalidDocblock', ], 'invalidTypeWhenNotImported' => [ 'code' => ' */ class C implements B {}', 'error_message' => 'UndefinedDocblockClass', ], 'invalidTypeWhenNotImportedInsideAnotherTypeAlias' => [ 'code' => ' */ class C implements B {}', 'error_message' => 'UndefinedDocblockClass', ], ]; } }