,error_levels?:string[]}> */ public function providerValidCodeParse(): iterable { return [ 'typeAliasBeforeClass' => [ ' [ ' [ ' [ ' [ ' */ 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' => [ ' "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' => [ ' "Nokia"]; } } /** * @psalm-import-type PhoneType from Phone as TPhone */ class User { /** @psalm-return TPhone */ function toArray(): array { return array_merge([], (new Phone)->toArray()); } }' ], 'classTypeAliasDirectUsage' => [ ' "Nokia"]; } } /** * @psalm-import-type PhoneType from Phone */ class User { /** @psalm-return PhoneType */ function toArray(): array { return array_merge([], (new Phone)->toArray()); } }' ], 'classTypeAliasFromExternalNamespace' => [ ' "Nokia"]; } } } namespace Bar { /** * @psalm-import-type PhoneType from \Foo\Phone */ class User { /** @psalm-return PhoneType */ function toArray(): array { return (new \Foo\Phone)->toArray(); } } }' ], 'importTypeForParam' => [ 'b($type); } /** * @psalm-param Type2 $type */ private function b(int $type): void { } }' ], 'usedInVarForForeach' => [ ' [ ' [ ' */ class C implements A { public function output() { return "hello"; } } $instance = new C(); $output = $instance->output();', [ '$output' => 'string', ], ], 'sameDocBlockTypeAliasAsTypeParameterForExtendedRegularClass' => [ 'value = $value; } } /** * @psalm-type Foo=string * @extends A */ class C extends A {} $instance = new C("hello"); $output = $instance->value;', [ '$output' => 'string', ], ], 'sameDocBlockTypeAliasAsTypeParameterForExtendedAbstractClass' => [ 'value = $value; } } /** * @psalm-type Foo=string * @extends A */ class C extends A {} $instance = new C("hello"); $output = $instance->value;', [ '$output' => 'string', ], ], 'importedTypeAliasAsTypeParameterForImplementation' => [ ' */ class C implements A {}', ], 'importedTypeAliasAsTypeParameterForExtendedClass' => [ ' */ class C extends A {}', ], 'importedTypeAliasAsTypeParameterForExtendedAbstractClass' => [ ' */ class C extends A {}', ], 'importedTypeAliasRenamedAsTypeParameterForImplementation' => [ ' */ class C implements A {}', ], 'importedTypeAliasRenamedAsTypeParameterForExtendedClass' => [ ' */ class C extends A {}', ], 'importedTypeAliasRenamedAsTypeParameterForExtendedAbstractClass' => [ ' */ class C extends A {}', ], 'importedTypeInsideLocalTypeAliasUsedAsTypeParameter' => [ '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;', [ '$output' => 'string', ], ], 'importedTypeWithPhpstanAnnotation' => [ '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;', [ '$output' => 'string', ], ], ]; } /** * @return iterable */ public function providerInvalidCodeParse(): iterable { return [ 'invalidTypeAlias' => [ ' */ class A {}', 'error_message' => 'InvalidDocblock', ], 'typeAliasInTKeyedArray' => [ ' 'InvalidReturnStatement', ], 'classTypeAliasInvalidReturn' => [ ' "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' => [ ' "Matt"]; } } /** * @psalm-import-type PhoneType from Phone */ class User {}', 'error_message' => 'InvalidTypeImport', ], 'classTypeAliasFromInvalidClass' => [ ' 'UndefinedDocblockClass', ], 'malformedImportMissingFrom' => [ ' 'InvalidTypeImport', ], 'malformedImportMissingSourceClass' => [ ' 'InvalidTypeImport', ], 'malformedImportMisspelledFrom' => [ ' 'InvalidTypeImport', ], 'malformedImportMissingAlias' => [ ' 'InvalidTypeImport', ], 'noCrashWithPriorReference' => [ ' 'UndefinedDocblockClass', ], 'mergeImportedTypes' => [ ' 'PossiblyUndefinedArrayOffset', ], 'noCrashWithSelfReferencingType' => [ ' 'InvalidDocblock', ], 'invalidTypeWhenNotImported' => [ ' */ class C implements B {}', 'error_message' => 'UndefinedDocblockClass', ], 'invalidTypeWhenNotImportedInsideAnotherTypeAlias' => [ ' */ class C implements B {}', 'error_message' => 'UndefinedDocblockClass', ], ]; } }