2016-12-11 23:41:11 -05:00
< ? php
namespace Psalm\Tests ;
2019-07-05 16:24:00 -04:00
use const DIRECTORY_SEPARATOR ;
2018-02-01 01:10:27 -05:00
use Psalm\Config ;
use Psalm\Context ;
2017-04-24 23:45:02 -04:00
class AnnotationTest extends TestCase
2016-12-11 23:41:11 -05:00
{
2018-11-05 21:57:36 -05:00
use Traits\InvalidCodeAnalysisTestTrait ;
use Traits\ValidCodeAnalysisTestTrait ;
2016-12-24 18:23:22 +00:00
2020-09-12 17:24:05 +02:00
public function testPhpStormGenericsWithValidArrayIteratorArgument () : void
2018-02-01 01:10:27 -05:00
{
Config :: getInstance () -> allow_phpstorm_generics = true ;
$this -> addFile (
'somefile.php' ,
' < ? php
function takesString ( string $s ) : void {}
/** @param ArrayIterator|string[] $i */
function takesArrayIteratorOfString ( ArrayIterator $i ) : void {
$s = $i -> offsetGet ( " a " );
takesString ( $s );
foreach ( $i as $s2 ) {
takesString ( $s2 );
}
} '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2020-09-12 17:24:05 +02:00
public function testPhpStormGenericsWithValidTraversableArgument () : void
2019-02-15 11:07:08 -05:00
{
Config :: getInstance () -> allow_phpstorm_generics = true ;
$this -> addFile (
'somefile.php' ,
' < ? php
function takesString ( string $s ) : void {}
/** @param Traversable|string[] $i */
function takesTraversableOfString ( Traversable $i ) : void {
foreach ( $i as $s2 ) {
takesString ( $s2 );
}
} '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2020-09-12 17:24:05 +02:00
public function testPhpStormGenericsWithClassProperty () : void
2018-03-17 15:40:57 -04:00
{
Config :: getInstance () -> allow_phpstorm_generics = true ;
$this -> addFile (
'somefile.php' ,
' < ? php
/** @psalm-suppress MissingConstructor */
class Foo {
2020-03-22 10:44:48 -04:00
/** @var \stdClass[]|\ArrayObject */
public $bar ;
/**
* @ return \stdClass [] | \ArrayObject
*/
public function getBar () : \ArrayObject {
return $this -> bar ;
}
2018-03-17 15:40:57 -04:00
} '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2020-09-12 17:24:05 +02:00
public function testPhpStormGenericsWithGeneratorArray () : void
2019-02-20 11:13:33 -05:00
{
Config :: getInstance () -> allow_phpstorm_generics = true ;
$this -> addFile (
'somefile.php' ,
' < ? php
class A {
/**
* @ return stdClass [] | Generator
*/
function getCollection () : Generator
{
yield new stdClass ;
}
} '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2020-09-12 17:24:05 +02:00
public function testPhpStormGenericsWithValidIterableArgument () : void
2018-03-01 11:19:23 -05:00
{
Config :: getInstance () -> allow_phpstorm_generics = true ;
$this -> addFile (
'somefile.php' ,
' < ? php
function takesString ( string $s ) : void {}
/** @param iterable|string[] $i */
function takesArrayIteratorOfString ( iterable $i ) : void {
foreach ( $i as $s2 ) {
takesString ( $s2 );
}
} '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2020-09-12 17:24:05 +02:00
public function testPhpStormGenericsInvalidArgument () : void
2018-02-01 01:10:27 -05:00
{
2019-05-16 18:36:36 -04:00
$this -> expectException ( \Psalm\Exception\CodeException :: class );
$this -> expectExceptionMessage ( 'InvalidScalarArgument' );
2018-02-01 01:10:27 -05:00
Config :: getInstance () -> allow_phpstorm_generics = true ;
$this -> addFile (
'somefile.php' ,
' < ? php
function takesInt ( int $s ) : void {}
/** @param ArrayIterator|string[] $i */
function takesArrayIteratorOfString ( ArrayIterator $i ) : void {
$s = $i -> offsetGet ( " a " );
takesInt ( $s );
} '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2020-09-12 17:24:05 +02:00
public function testPhpStormGenericsNoTypehint () : void
2018-02-01 01:10:27 -05:00
{
2019-05-16 18:36:36 -04:00
$this -> expectException ( \Psalm\Exception\CodeException :: class );
$this -> expectExceptionMessage ( 'PossiblyInvalidMethodCall' );
2018-02-01 01:10:27 -05:00
Config :: getInstance () -> allow_phpstorm_generics = true ;
$this -> addFile (
'somefile.php' ,
' < ? php
/** @param ArrayIterator|string[] $i */
function takesArrayIteratorOfString ( $i ) : void {
$s = $i -> offsetGet ( " a " );
} '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2020-09-12 17:24:05 +02:00
public function testInvalidParamDefault () : void
2018-06-07 12:23:10 -04:00
{
2019-05-16 18:36:36 -04:00
$this -> expectException ( \Psalm\Exception\CodeException :: class );
$this -> expectExceptionMessage ( 'InvalidParamDefault' );
2018-06-07 12:23:10 -04:00
$this -> addFile (
'somefile.php' ,
' < ? php
/**
* @ param array $arr
* @ return void
*/
function foo ( $arr = false ) {} '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2020-09-12 17:24:05 +02:00
public function testInvalidParamDefaultButAllowedInConfig () : void
2018-06-07 12:23:10 -04:00
{
Config :: getInstance () -> add_param_default_to_docblock_type = true ;
$this -> addFile (
'somefile.php' ,
' < ? php
/**
* @ param array $arr
* @ return void
*/
function foo ( $arr = false ) {}
foo ( false );
foo ([ " hello " ]); '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2020-09-12 17:24:05 +02:00
public function testInvalidTypehintParamDefaultButAllowedInConfig () : void
2018-06-07 12:23:10 -04:00
{
2019-05-16 18:36:36 -04:00
$this -> expectException ( \Psalm\Exception\CodeException :: class );
$this -> expectExceptionMessage ( 'InvalidParamDefault' );
2018-06-07 12:23:10 -04:00
Config :: getInstance () -> add_param_default_to_docblock_type = true ;
$this -> addFile (
'somefile.php' ,
' < ? php
function foo ( array $arr = false ) : void {} '
);
$this -> analyzeFile ( 'somefile.php' , new Context ());
}
2017-03-19 14:39:05 -04:00
/**
2019-03-01 22:55:20 +02:00
* @ return iterable < string , array { string , assertions ? : array < string , string > , error_levels ? : string []} >
2017-04-24 23:45:02 -04:00
*/
2020-09-12 17:24:05 +02:00
public function providerValidCodeParse () : iterable
2017-04-24 23:45:02 -04:00
{
return [
2018-01-21 10:22:04 -05:00
'nopType' => [
' < ? php
$a = " hello " ;
/** @var int $a */ ' ,
'assertions' => [
'$a' => 'int' ,
],
],
2017-04-24 23:45:02 -04:00
'validDocblockReturn' => [
' < ? php
/**
* @ return string
*/
2018-01-11 15:50:45 -05:00
function fooFoo () : string {
2017-04-24 23:45:02 -04:00
return " boop " ;
}
2017-05-04 18:35:05 -04:00
2017-04-24 23:45:02 -04:00
/**
* @ return array < int , string >
*/
2018-01-11 15:50:45 -05:00
function foo2 () : array {
2017-04-24 23:45:02 -04:00
return [ " hello " ];
}
2017-05-04 18:35:05 -04:00
2017-04-24 23:45:02 -04:00
/**
* @ return array < int , string >
*/
2018-01-11 15:50:45 -05:00
function foo3 () : array {
2017-04-24 23:45:02 -04:00
return [ " hello " ];
2017-05-26 20:05:57 -04:00
} ' ,
2017-04-24 23:45:02 -04:00
],
'reassertWithIs' => [
' < ? php
/** @param array $a */
2018-01-11 15:50:45 -05:00
function foo ( $a ) : void {
2017-04-24 23:45:02 -04:00
if ( is_array ( $a )) {
// do something
}
2017-05-26 20:05:57 -04:00
} ' ,
2018-02-06 18:44:53 -05:00
'assertions' => [],
2018-02-07 15:20:47 -05:00
'error_level' => [ 'RedundantConditionGivenDocblockType' ],
2017-04-24 23:45:02 -04:00
],
'checkArrayWithIs' => [
' < ? php
/** @param mixed $b */
2018-01-11 15:50:45 -05:00
function foo ( $b ) : void {
2017-04-24 23:45:02 -04:00
/** @var array */
$a = ( array ) $b ;
if ( is_array ( $a )) {
// do something
}
2017-05-26 20:05:57 -04:00
} ' ,
2018-02-06 18:44:53 -05:00
'assertions' => [],
2018-02-07 15:20:47 -05:00
'error_level' => [ 'RedundantConditionGivenDocblockType' ],
2017-04-24 23:45:02 -04:00
],
'goodDocblock' => [
' < ? php
class A {
/**
* @ param A $a
* @ param bool $b
*/
2018-01-11 15:50:45 -05:00
public function g ( A $a , $b ) : void {
2017-04-24 23:45:02 -04:00
}
2017-05-26 20:05:57 -04:00
} ' ,
2017-04-24 23:45:02 -04:00
],
'goodDocblockInNamespace' => [
' < ? php
namespace Foo ;
2017-05-04 18:35:05 -04:00
2017-04-24 23:45:02 -04:00
class A {
/**
* @ param \Foo\A $a
* @ param bool $b
*/
2018-01-11 15:50:45 -05:00
public function g ( A $a , $b ) : void {
2017-04-24 23:45:02 -04:00
}
2017-05-26 20:05:57 -04:00
} ' ,
2017-05-04 18:35:05 -04:00
],
2017-05-10 12:36:11 -04:00
'ignoreNullableReturn' => [
' < ? php
class A {
/** @var int */
public $bar = 5 ;
2018-01-11 15:50:45 -05:00
public function foo () : void {}
2017-05-10 12:36:11 -04:00
}
/**
* @ return ? A
* @ psalm - ignore - nullable - return
*/
function makeA () {
2018-01-11 15:50:45 -05:00
return rand ( 0 , 1 ) ? new A () : null ;
2017-05-10 12:36:11 -04:00
}
2018-01-11 15:50:45 -05:00
function takeA ( A $a ) : void { }
2017-05-10 12:36:11 -04:00
$a = makeA ();
$a -> foo ();
$a -> bar = 7 ;
2017-05-26 20:05:57 -04:00
takeA ( $a ); ' ,
2017-05-10 12:36:11 -04:00
],
2017-06-13 14:00:41 -04:00
'invalidDocblockParamSuppress' => [
' < ? php
/**
* @ param int $bar
2018-01-05 12:11:12 -05:00
* @ psalm - suppress MismatchingDocblockParamType
2017-06-13 14:00:41 -04:00
*/
2018-01-11 15:50:45 -05:00
function fooFoo ( array $bar ) : void {
2017-06-13 14:00:41 -04:00
} ' ,
],
2017-07-25 16:11:02 -04:00
'differentDocblockParamClassSuppress' => [
' < ? php
class A {}
2018-05-21 00:46:56 -04:00
class B {}
2017-07-25 16:11:02 -04:00
/**
* @ param B $bar
2018-01-05 12:11:12 -05:00
* @ psalm - suppress MismatchingDocblockParamType
2017-07-25 16:11:02 -04:00
*/
2018-01-11 15:50:45 -05:00
function fooFoo ( A $bar ) : void {
2017-07-25 16:11:02 -04:00
} ' ,
],
'varDocblock' => [
' < ? php
/** @var array<Exception> */
$a = [];
$a [ 0 ] -> getMessage (); ' ,
],
2017-09-02 11:18:56 -04:00
'mixedDocblockParamTypeDefinedInParent' => [
' < ? php
class A {
/** @param mixed $a */
2018-01-11 15:50:45 -05:00
public function foo ( $a ) : void {}
2017-09-02 11:18:56 -04:00
}
class B extends A {
2018-01-11 15:50:45 -05:00
public function foo ( $a ) : void {}
2017-09-02 11:18:56 -04:00
} ' ,
],
'intDocblockParamTypeDefinedInParent' => [
' < ? php
class A {
/** @param int $a */
2018-01-11 15:50:45 -05:00
public function foo ( $a ) : void {}
2017-09-02 11:18:56 -04:00
}
class B extends A {
2018-01-11 15:50:45 -05:00
public function foo ( $a ) : void {}
2017-09-02 11:18:56 -04:00
} ' ,
],
2017-10-07 10:22:52 -04:00
'varSelf' => [
' < ? php
class A
{
2018-01-11 15:50:45 -05:00
public function foo () : void {}
2017-10-07 10:22:52 -04:00
2018-01-11 15:50:45 -05:00
public function getMeAgain () : void {
2017-10-07 10:22:52 -04:00
/** @var self */
$me = $this ;
$me -> foo ();
}
} ' ,
],
2017-11-02 21:45:17 -04:00
'psalmVar' => [
' < ? php
class A
{
/** @psalm-var array<int, string> */
public $foo = [];
2018-01-11 15:50:45 -05:00
public function updateFoo () : void {
2017-11-02 21:45:17 -04:00
$this -> foo [ 5 ] = " hello " ;
}
} ' ,
],
'psalmParam' => [
' < ? php
2018-01-11 15:50:45 -05:00
function takesInt ( int $a ) : void {}
2017-11-02 21:45:17 -04:00
/**
* @ psalm - param array < int , string > $a
* @ param string [] $a
*/
2018-01-11 15:50:45 -05:00
function foo ( array $a ) : void {
2017-11-02 21:45:17 -04:00
foreach ( $a as $key => $value ) {
takesInt ( $key );
}
} ' ,
],
2017-12-30 10:54:01 -05:00
'returnDocblock' => [
' < ? php
2018-01-11 15:50:45 -05:00
function foo ( int $i ) : int {
2017-12-30 10:54:01 -05:00
/** @var int */
return $i ;
} ' ,
],
2018-02-07 23:33:31 -05:00
'doubleVar' => [
' < ? php
function foo () : array {
return [ " hello " => new stdClass , " goodbye " => new stdClass ];
}
$a = null ;
$b = null ;
/**
* @ var string $key
* @ var stdClass $value
*/
foreach ( foo () as $key => $value ) {
$a = $key ;
$b = $value ;
} ' ,
'assertions' => [
'$a' => 'null|string' ,
'$b' => 'null|stdClass' ,
],
],
2018-03-21 10:46:21 -04:00
'allowOptionalParamsToBeEmptyArray' => [
' < ? php
/** @param array{b?: int, c?: string} $a */
function foo ( array $a = []) : void {} ' ,
],
2018-03-30 17:46:12 -04:00
'allowEmptyVarAnnotation' => [
' < ? php
/**
* @ param $x
*/
function example ( array $x ) : void {} ' ,
],
2018-04-04 12:39:05 -04:00
'allowCapitalisedNamespacedString' => [
' < ? php
namespace Foo ;
/**
* @ param String $x
*/
function example ( string $x ) : void {} ' ,
],
2018-04-20 15:22:48 -04:00
'megaClosureAnnotationWithoutSpacing' => [
' < ? php
/** @var array{a:Closure():(array<mixed, mixed>|null), b?:Closure():array<mixed, mixed>, c?:Closure():array<mixed, mixed>, d?:Closure():array<mixed, mixed>, e?:Closure():(array{f:null|string, g:null|string, h:null|string, i:string, j:mixed, k:mixed, l:mixed, m:mixed, n:bool, o?:array{0:string}}|null), p?:Closure():(array{f:null|string, g:null|string, h:null|string, q:string, i:string, j:mixed, k:mixed, l:mixed, m:mixed, n:bool, o?:array{0:string}}|null), r?:Closure():(array<mixed, mixed>|null), s:array<mixed, mixed>} */
$arr = [];
2018-09-17 12:15:45 -04:00
$arr [ " a " ](); ' ,
2018-04-20 15:22:48 -04:00
],
'megaClosureAnnotationWithSpacing' => [
' < ? php
2019-06-16 12:39:07 -04:00
/**
* @ var array {
* a : Closure () : ( array < mixed , mixed >| null ),
* b ? : Closure () : array < mixed , mixed > ,
* c ? : Closure () : array < mixed , mixed > ,
* d ? : Closure () : array < mixed , mixed > ,
* e ? : Closure () : ( array {
* f : null | string ,
* g : null | string ,
* h : null | string ,
* i : string ,
* j : mixed ,
* k : mixed ,
* l : mixed ,
* m : mixed ,
* n : bool ,
* o ? : array { 0 : string }
* } | null ),
* p ? : Closure () : ( array {
* f : null | string ,
* g : null | string ,
* h : null | string ,
* q : string ,
* i : string ,
* j : mixed ,
* k : mixed ,
* l : mixed ,
* m : mixed ,
* n : bool ,
* o ? : array { 0 : string }
* } | null ),
* r ? : Closure () : ( array < mixed , mixed >| null ),
* s : array < mixed , mixed >
* }
*
* Some text
*/
2018-04-20 15:22:48 -04:00
$arr = [];
2018-09-17 12:15:45 -04:00
$arr [ " a " ](); ' ,
2018-04-20 15:22:48 -04:00
],
2019-06-16 12:39:07 -04:00
'multipeLineGenericArray' => [
' < ? php
/**
* @ psalm - type MiddlewareArray = array <
* class - string < \Exception > ,
* array < int , string >
* >
*
* @ psalm - type RuleArray = array {
* rule : string ,
* controller ? : class - string < \Exception > ,
* redirect ? : string ,
* code ? : int ,
* type ? : string ,
* middleware ? : MiddlewareArray
* }
*
* Foo Bar
*/
class A {} ' ,
],
2019-11-19 02:01:12 +02:00
'builtInClassInAShape' => [
' < ? php
2020-03-22 10:44:48 -04:00
/**
* @ return array { d : Exception }
* @ psalm - suppress InvalidReturnType
*/
function f () {} '
2019-11-19 02:01:12 +02:00
],
2018-05-21 12:55:44 -04:00
'slashAfter?' => [
' < ? php
namespace ns ;
/** @param ?\stdClass $s */
function foo ( $s ) : void {
}
foo ( null );
foo ( new \stdClass ); ' ,
],
2018-05-29 09:44:38 -04:00
'returnTypeShouldBeNullable' => [
' < ? php
/**
* @ return stdClass
*/
function foo () : ? stdClass {
return rand ( 0 , 1 ) ? new stdClass : null ;
}
$f = foo ();
2019-03-23 14:27:54 -04:00
if ( $f ) {} ' ,
2018-05-29 09:44:38 -04:00
],
2019-04-09 13:58:49 -04:00
'spreadOperatorAnnotation' => [
2018-06-08 21:18:49 -04:00
' < ? php
/** @param string[] $s */
2019-04-09 13:58:49 -04:00
function foo ( string ... $s ) : void {}
/** @param string ...$s */
function bar ( string ... $s ) : void {}
foo ( " hello " , " goodbye " );
bar ( " hello " , " goodbye " );
foo ( ... [ " hello " , " goodbye " ]);
bar ( ... [ " hello " , " goodbye " ]); ' ,
2018-06-08 21:18:49 -04:00
],
2019-04-09 14:23:48 -04:00
'spreadOperatorByRefAnnotation' => [
' < ? php
/** @param string &...$s */
function foo ( &... $s ) : void {}
/** @param string ...&$s */
function bar ( &... $s ) : void {}
/** @param string[] &$s */
function bat ( &... $s ) : void {}
$a = " hello " ;
$b = " goodbye " ;
$c = " hello again " ;
foo ( $a );
2019-04-09 14:29:09 -04:00
bar ( $b );
bat ( $c ); ' ,
2019-04-09 14:23:48 -04:00
'assertions' => [
'$a' => 'string' ,
'$b' => 'string' ,
'$c' => 'string' ,
],
],
2018-07-05 17:25:26 -04:00
'valueReturnType' => [
' < ? php
/**
* @ param " a " | " b " $_p
*/
function acceptsLiteral ( $_p ) : void {}
/**
* @ return " a " | " b "
*/
function returnsLiteral () : string {
return rand ( 0 , 1 ) ? " a " : " b " ;
}
2019-03-23 14:27:54 -04:00
acceptsLiteral ( returnsLiteral ()); ' ,
2018-07-05 17:25:26 -04:00
],
2018-07-15 17:23:17 -04:00
'typeAliasBeforeClass' => [
' < ? php
/**
* @ psalm - type CoolType = A | B | null
*/
class A {}
class B {}
/** @return CoolType */
function foo () {
if ( rand ( 0 , 1 )) {
return new A ();
}
if ( rand ( 0 , 1 )) {
return new B ();
}
return null ;
}
/** @param CoolType $a **/
function bar ( $a ) : void { }
2019-03-23 14:27:54 -04:00
bar ( foo ()); ' ,
2018-07-15 17:23:17 -04:00
],
'typeAliasBeforeFunction' => [
' < ? php
/**
2018-09-04 22:27:55 -04:00
* @ psalm - type A_OR_B = A | B
* @ psalm - type CoolType = A_OR_B | null
2018-07-15 17:23:17 -04:00
* @ return CoolType
*/
function foo () {
if ( rand ( 0 , 1 )) {
return new A ();
2018-07-21 22:24:33 -04:00
}
if ( rand ( 0 , 1 )) {
return new B ();
}
return null ;
}
class A {}
class B {}
/** @param CoolType $a **/
function bar ( $a ) : void { }
2019-03-23 14:27:54 -04:00
bar ( foo ()); ' ,
2018-07-21 22:24:33 -04:00
],
'typeAliasInSeparateBlockBeforeFunction' => [
' < ? php
/**
* @ psalm - type CoolType = A | B | null
*/
/**
* @ return CoolType
*/
function foo () {
if ( rand ( 0 , 1 )) {
return new A ();
2018-07-15 17:23:17 -04:00
}
if ( rand ( 0 , 1 )) {
return new B ();
}
return null ;
}
class A {}
class B {}
/** @param CoolType $a **/
function bar ( $a ) : void { }
2019-03-23 14:27:54 -04:00
bar ( foo ()); ' ,
2018-07-15 17:23:17 -04:00
],
'almostFreeStandingTypeAlias' => [
' < ? php
/**
* @ psalm - type CoolType = A | B | null
*/
// this breaks up the line
class A {}
class B {}
/** @return CoolType */
function foo () {
if ( rand ( 0 , 1 )) {
return new A ();
}
if ( rand ( 0 , 1 )) {
return new B ();
}
return null ;
}
/** @param CoolType $a **/
function bar ( $a ) : void { }
2019-03-23 14:27:54 -04:00
bar ( foo ()); ' ,
2018-07-15 17:23:17 -04:00
],
2018-09-24 13:08:23 -04:00
'typeAliasUsedTwice' => [
' < ? php
/** @psalm-type TA = array<int, string> */
class Bar {
public function foo () : void {
$bar =
/** @return TA */
function () {
return [ " hello " ];
};
/** @var array<int, TA> */
$bat = [ $bar (), $bar ()];
foreach ( $bat as $b ) {
echo $b [ 0 ];
}
}
}
/**
2020-03-22 10:44:48 -04:00
* @ psalm - type _A = array { elt : int }
* @ param _A $p
* @ return _A
*/
2018-09-24 13:08:23 -04:00
function f ( $p ) {
/** @var _A */
$r = $p ;
return $r ;
} ' ,
],
2018-08-20 22:11:01 -04:00
'listUnpackWithDocblock' => [
' < ? php
interface I {}
class A implements I {
public function bar () : void {}
}
/** @return I[] */
function foo () : array {
return [ new A ()];
}
/** @var A $a1 */
[ $a1 , $a2 ] = foo ();
$a1 -> bar (); ' ,
],
2018-08-22 22:53:44 -04:00
'spaceInType' => [
' < ? php
/** @return string | null */
function foo ( string $s = null ) {
return $s ;
} ' ,
],
2018-11-01 17:03:08 -04:00
'missingReturnTypeWithBadDocblockIgnoreBoth' => [
' < ? php
/**
* @ return [ bad ]
*/
function fooBar () {
} ' ,
[],
[
'InvalidDocblock' => \Psalm\Config :: REPORT_INFO ,
'MissingReturnType' => \Psalm\Config :: REPORT_INFO ,
2019-03-23 14:27:54 -04:00
],
2018-11-01 17:03:08 -04:00
],
2019-01-18 00:56:24 -05:00
'objectWithPropertiesAnnotation' => [
' < ? php
/** @param object{foo:string} $o */
function foo ( object $o ) : string {
return $o -> foo ;
}
$s = new \stdClass ();
$s -> foo = " hello " ;
foo ( $s );
class A {
/** @var string */
public $foo = " hello " ;
}
foo ( new A ); ' ,
],
2019-01-19 11:31:51 -05:00
'refineTypeInNestedCall' => [
' < ? php
function foo ( array $arr ) : \Generator {
/** @var array<string, mixed> $arr */
foreach ( array_filter ( array_keys ( $arr ), function ( string $key ) : bool {
return strpos ( $key , " BAR " ) === 0 ;
}) as $envVar ) {
yield $envVar => [ getenv ( $envVar )];
}
2019-03-23 14:27:54 -04:00
} ' ,
2019-01-19 11:31:51 -05:00
],
'allowAnnotationOnServer' => [
' < ? php
function foo () : \Generator {
/** @var array<string, mixed> $_SERVER */
foreach ( array_filter ( array_keys ( $_SERVER ), function ( string $key ) : bool {
return strpos ( $key , " BAR " ) === 0 ;
}) as $envVar ) {
yield $envVar => [ getenv ( $envVar )];
}
2019-03-23 14:27:54 -04:00
} ' ,
2019-01-19 11:31:51 -05:00
],
2019-01-20 11:49:13 -05:00
'annotationOnForeachItems' => [
' < ? php
function foo ( array $arr ) : void {
$item = null ;
/** @var string $item */
foreach ( $arr as $item ) {}
if ( is_null ( $item )) {}
}
function bar ( array $arr ) : void {
$item = null ;
/** @var string $item */
foreach ( $arr as $item => $_ ) {}
if ( is_null ( $item )) {}
}
function bat ( array $arr ) : void {
$item = null ;
2020-05-02 23:37:59 -04:00
/**
* @ psalm - suppress MixedArrayAccess
* @ var string $item
*/
2019-01-20 11:49:13 -05:00
foreach ( $arr as list ( $item )) {}
if ( is_null ( $item )) {}
}
function baz ( array $arr ) : void {
$item = null ;
2020-05-02 23:37:59 -04:00
/**
* @ psalm - suppress MixedArrayAccess
* @ var string $item
*/
2019-01-20 11:49:13 -05:00
foreach ( $arr as list ( $item => $_ )) {}
if ( is_null ( $item )) {}
} ' ,
[],
[
2019-03-23 14:27:54 -04:00
'MixedAssignment' ,
],
2019-01-20 11:49:13 -05:00
],
2019-02-23 11:39:00 -05:00
'extraneousDocblockParamName' => [
' < ? php
/**
* @ param string $foo
* @ param string [] $bar
* @ param string [] $barb
*/
function f ( string $foo , array $barb ) : void {} ' ,
],
2019-02-27 09:08:27 -05:00
'nonEmptyArray' => [
' < ? php
/** @param non-empty-array<string> $arr */
function foo ( array $arr ) : void {
foreach ( $arr as $a ) {}
echo $a ;
}
foo ([ " a " , " b " , " c " ]);
2019-03-02 08:35:50 -05:00
/** @param array<string> $arr */
function bar ( array $arr ) : void {
if ( ! $arr ) {
return ;
}
foo ( $arr );
2019-03-23 14:27:54 -04:00
} ' ,
2019-03-02 08:35:50 -05:00
],
'nonEmptyArrayInNamespace' => [
' < ? php
namespace ns ;
/** @param non-empty-array<string> $arr */
function foo ( array $arr ) : void {
foreach ( $arr as $a ) {}
echo $a ;
}
foo ([ " a " , " b " , " c " ]);
2019-02-27 09:08:27 -05:00
/** @param array<string> $arr */
function bar ( array $arr ) : void {
if ( ! $arr ) {
return ;
}
foo ( $arr );
2019-03-23 14:27:54 -04:00
} ' ,
2019-02-27 09:08:27 -05:00
],
2019-05-15 22:30:35 -04:00
'noExceptionOnIntersection' => [
' < ? php
class Foo {
/** @var null|\DateTime&\DateTimeImmutable */
private $s = null ;
} ' ,
],
2019-05-16 13:52:58 -04:00
'intersectionWithSpace' => [
' < ? php
interface A {
public function foo () : void ;
}
interface B {
public function bar () : void ;
}
/** @param A & B $a */
function f ( A $a ) : void {
$a -> foo ();
$a -> bar ();
2019-07-05 16:24:00 -04:00
} ' ,
2019-05-16 13:52:58 -04:00
],
2019-06-18 17:47:06 -04:00
'allowClosingComma' => [
' < ? php
2020-03-05 17:31:58 +00:00
/**
* @ psalm - type _Alias = array {
* foo : string ,
* bar : string ,
* baz : array {
* a : int ,
* },
* }
*/
class Foo { }
2019-06-18 17:47:06 -04:00
/**
* @ param array {
* foo : string ,
* bar : string ,
* baz : array {
* a : int ,
* },
* } $foo
*/
function foo ( array $foo ) : int {
2020-03-22 10:44:48 -04:00
return count ( $foo );
2019-06-18 17:47:06 -04:00
}
/**
* @ var array {
* foo : string ,
* bar : string ,
* baz : string ,
* } $foo
*/
2019-07-05 16:24:00 -04:00
$foo = [ " foo " => " " , " bar " => " " , " baz " => " " ]; ' ,
2019-06-18 17:47:06 -04:00
],
2019-11-21 11:03:18 -05:00
'returnNumber' => [
' < ? php
class C {
/**
* @ return 1
*/
public static function barBar () {
return 1 ;
}
} ' ,
],
'returnNumberForInterface' => [
' < ? php
interface I {
/**
* @ return 1
*/
public static function barBar ();
} ' ,
],
2019-12-11 10:30:40 -05:00
'psalmTypeAnnotationAboveReturn' => [
' < ? php
/**
* @ psalm - type Person = array { name : string , age : int }
*/
/**
* @ psalm - return Person
*/
function getPerson_error () : array {
$json = \ ' { " name " : " John " , " age " : 44 } \ ' ;
/** @psalm-var Person */
return json_decode ( $json , true );
} '
],
2020-08-30 11:44:14 -04:00
'allowDocblockDefinedTKeyedArrayIntoNonEmpty' => [
2020-01-13 21:02:47 -05:00
' < ? php
/** @param non-empty-array $_bar */
function foo ( array $_bar ) : void { }
/** @var array{0:list<string>, 1:list<int>} */
$bar = [[], []];
foo ( $bar ); '
],
2020-01-14 22:46:19 -05:00
'allowResourceInList' => [
' < ? php
/** @param list<scalar|array|object|resource|null> $_s */
function foo ( array $_s ) : void { } '
],
2020-01-23 14:52:35 -05:00
'possiblyUndefinedObjectProperty' => [
' < ? php
function consume ( string $value ) : void {
2020-03-22 10:44:48 -04:00
echo $value ;
2020-01-23 14:52:35 -05:00
}
/** @var object{value?: string} $data */
$data = json_decode ( " { } " , false );
consume ( $data -> value ? ? " " ); '
],
2020-01-30 18:00:31 -05:00
'throwSelf' => [
' < ? php
2020-02-13 13:01:53 -05:00
namespace Foo ;
2020-01-30 18:00:31 -05:00
class MyException extends \Exception {
/**
* @ throws self
*/
2020-03-22 10:44:48 -04:00
public static function create () : void {
throw new self ();
2020-01-30 18:00:31 -05:00
}
} '
],
2020-02-12 09:18:43 -05:00
'parseTrailingCommaInReturn' => [
' < ? php
/**
* @ psalm - return array {
* a : int ,
* b : string ,
* }
*/
function foo () : array {
return [ " a " => 1 , " b " => " two " ];
} '
],
2020-03-09 15:35:02 +00:00
'falsableFunctionAllowedWhenBooleanExpected' => [
' < ? php
/** @psalm-return bool */
function alwaysFalse1 ()
{
return false ;
}
function alwaysFalse2 () : bool
{
return false ;
} '
],
2020-04-20 09:22:58 -04:00
'dontInheritDocblockReturnWhenRedeclared' => [
' < ? php
interface Id {}
class UserId implements Id {}
interface Entity {
/** @psalm-return Id */
function id () : Id ;
}
class User implements Entity {
public function id () : UserId {
return new UserId ();
}
} ' ,
[],
[],
'7.4'
],
2020-04-26 17:36:02 -04:00
'arrayWithKeySlashesAndNewline' => [
' < ? php
$arr = [ " foo \\ bar \n baz " => " literal " ]; ' ,
[
'$arr' => 'array{\'foo\\\\bar\nbaz\': string}'
]
],
2020-05-02 19:39:10 -04:00
'doubleSpaceBeforeAt' => [
' < ? php
/**
* @ param string $c
*/
function foo ( $c ) : void {} '
],
2020-06-01 14:56:27 -04:00
'throwsAnnotationWithBarAndSpace' => [
' < ? php
/**
* @ throws \Exception | \InvalidArgumentException
*/
function bar () : void {} '
],
2020-07-26 13:23:21 -04:00
'varDocblockAboveCall' => [
' < ? php
function example ( string $s ) : void {
if ( preg_match ( \ ' { foo - ( \w + )} \ ' , $s , $m )) {
/** @var array{string, string} $m */
takesString ( $m [ 1 ]);
}
}
function takesString ( string $s ) : void {} '
],
2020-09-01 00:12:12 -04:00
'noCrashWithoutAssignment' => [
' < ? php
/** @var DateTime $obj */
echo $obj -> format ( " Y " ); '
],
2020-11-02 00:40:36 -05:00
'intMaskWithClassConstants' => [
' < ? php
class FileFlag {
public const OPEN = 1 ;
public const MODIFIED = 2 ;
public const NEW = 4 ;
}
/**
* @ param int - mask < FileFlag :: OPEN , FileFlag :: MODIFIED , FileFlag :: NEW > $flags
*/
function takesFlags ( int $flags ) : void {
echo $flags ;
}
takesFlags ( FileFlag :: MODIFIED | FileFlag :: NEW ); '
],
'intMaskOfWithClassWildcard' => [
' < ? php
class FileFlag {
public const OPEN = 1 ;
public const MODIFIED = 2 ;
public const NEW = 4 ;
}
/**
* @ param int - mask - of < FileFlag ::*> $flags
*/
function takesFlags ( int $flags ) : void {
echo $flags ;
}
takesFlags ( FileFlag :: MODIFIED | FileFlag :: NEW ); '
],
2017-04-24 23:45:02 -04:00
];
}
/**
2019-03-01 22:55:20 +02:00
* @ return iterable < string , array { string , error_message : string , 2 ? : string [], 3 ? : bool , 4 ? : string } >
2017-04-24 23:45:02 -04:00
*/
2020-09-12 17:24:05 +02:00
public function providerInvalidCodeParse () : iterable
2017-04-24 23:45:02 -04:00
{
return [
2019-05-16 18:36:36 -04:00
'invalidClassMethodReturn' => [
' < ? php
class C {
/**
* @ return $thus
*/
public function barBar () {
return $this ;
}
} ' ,
'error_message' => 'MissingDocblockType' ,
],
2019-11-21 11:03:18 -05:00
2019-05-16 18:36:36 -04:00
'invalidClassMethodReturnBrackets' => [
' < ? php
class C {
/**
* @ return []
*/
public static function barBar () {
return [];
}
} ' ,
'error_message' => 'InvalidDocblock' ,
],
'invalidInterfaceMethodReturn' => [
2017-05-24 21:11:18 -04:00
' < ? php
interface I {
/**
* @ return $thus
*/
public static function barBar ();
} ' ,
2017-11-14 21:43:31 -05:00
'error_message' => 'MissingDocblockType' ,
2017-05-24 21:11:18 -04:00
],
2019-05-16 18:36:36 -04:00
'invalidInterfaceMethodReturnBrackets' => [
2018-03-24 21:02:44 -04:00
' < ? php
interface I {
/**
* @ return []
*/
public static function barBar ();
} ' ,
'error_message' => 'InvalidDocblock' ,
],
'invalidPropertyBrackets' => [
' < ? php
class A {
/**
* @ var []
*/
public $bar ;
} ' ,
'error_message' => 'InvalidDocblock' ,
],
2017-07-09 20:32:35 -04:00
'invalidReturnClassWithComma' => [
' < ? php
interface I {
/**
* @ return 1 ,
*/
public static function barBar ();
} ' ,
'error_message' => 'InvalidDocblock' ,
],
'returnClassWithComma' => [
' < ? php
interface I {
/**
* @ return a ,
*/
public static function barBar ();
} ' ,
'error_message' => 'InvalidDocblock' ,
],
2017-04-24 23:45:02 -04:00
'missingParamType' => [
' < ? php
/**
2017-07-25 16:11:02 -04:00
* @ param string $bar
2017-04-24 23:45:02 -04:00
*/
2018-01-11 15:50:45 -05:00
function fooBar () : void {
2017-07-25 16:11:02 -04:00
}
fooBar ( " hello " ); ' ,
2020-03-15 13:44:00 -04:00
'error_message' => 'TooManyArguments - src' . DIRECTORY_SEPARATOR . 'somefile.php:8:21 - Too many arguments for fooBar '
2018-02-02 11:26:55 -05:00
. '- expecting 0 but saw 1' ,
2017-04-24 23:45:02 -04:00
],
'missingParamVar' => [
' < ? php
/**
* @ param string
*/
2018-01-11 15:50:45 -05:00
function fooBar () : void {
2017-04-24 23:45:02 -04:00
} ' ,
2019-02-27 16:00:44 -05:00
'error_message' => 'InvalidDocblock - src' . DIRECTORY_SEPARATOR . 'somefile.php:5:21 - Badly-formatted @param' ,
2017-04-24 23:45:02 -04:00
],
2019-08-01 16:10:12 -04:00
'invalidSlashWithString' => [
' < ? php
/**
* @ return \ ? string
*/
function foo () {
2020-03-22 10:44:48 -04:00
return rand ( 0 , 1 ) ? " hello " : null ;
2019-08-01 16:10:12 -04:00
} ' ,
'error_message' => 'InvalidDocblock' ,
],
2018-11-01 17:03:08 -04:00
'missingReturnTypeWithBadDocblock' => [
' < ? php
/**
* @ return [ bad ]
*/
function fooBar () {
} ' ,
'error_message' => 'MissingReturnType' ,
[
'InvalidDocblock' => \Psalm\Config :: REPORT_INFO ,
2019-03-23 14:27:54 -04:00
],
2018-11-01 17:03:08 -04:00
],
2017-04-24 23:45:02 -04:00
'invalidDocblockReturn' => [
' < ? php
/**
* @ return string
*/
2018-01-11 15:50:45 -05:00
function fooFoo () : int {
2017-07-25 16:11:02 -04:00
return 5 ;
2017-04-24 23:45:02 -04:00
} ' ,
2018-01-05 12:11:12 -05:00
'error_message' => 'MismatchingDocblockReturnType' ,
2017-05-04 18:35:05 -04:00
],
2017-09-02 11:18:56 -04:00
'intParamTypeDefinedInParent' => [
' < ? php
class A {
2018-01-11 15:50:45 -05:00
public function foo ( int $a ) : void {}
2017-09-02 11:18:56 -04:00
}
class B extends A {
2018-01-11 15:50:45 -05:00
public function foo ( $a ) : void {}
2017-09-02 11:18:56 -04:00
} ' ,
2018-01-28 20:03:47 -05:00
'error_message' => 'MissingParamType' ,
2017-09-02 11:18:56 -04:00
'error_levels' => [ 'MethodSignatureMismatch' ],
],
2017-11-02 21:45:17 -04:00
'psalmInvalidVar' => [
' < ? php
class A
{
/** @psalm-var array<int, string> */
public $foo = [];
2018-01-11 15:50:45 -05:00
public function updateFoo () : void {
2017-11-02 21:45:17 -04:00
$this -> foo [ " boof " ] = " hello " ;
}
} ' ,
2018-01-11 14:38:24 -08:00
'error_message' => 'InvalidPropertyAssignmentValue' ,
2017-11-02 21:45:17 -04:00
],
2017-11-14 21:43:31 -05:00
'incorrectDocblockOrder' => [
' < ? php
class MyClass {
/**
* Comment
* @ var $fooPropTypo string
*/
public $fooProp = " /tmp/file.txt " ;
} ' ,
'error_message' => 'MissingDocblockType' ,
],
'badlyFormattedVar' => [
' < ? php
/**
* @ return string []
*/
function returns_strings () {
/** @var array(string) $result */
$result = [ " example " ];
return $result ;
} ' ,
'error_message' => 'InvalidDocblock' ,
],
'badlyWrittenVar' => [
' < ? php
/** @param mixed $x */
2018-01-11 15:50:45 -05:00
function myvalue ( $x ) : void {
2017-11-14 21:43:31 -05:00
/** @var $myVar MyNS\OtherClass */
$myVar = $x -> conn () -> method ();
$myVar -> otherMethod ();
} ' ,
'error_message' => 'MissingDocblockType' ,
],
2018-01-08 17:17:49 -05:00
'dontOverrideSameType' => [
' < ? php
class A {
/** @return ?int */
2018-01-11 15:50:45 -05:00
public function foo () : ? int {
2018-01-08 17:17:49 -05:00
if ( rand ( 0 , 1 )) return 5 ;
}
} ' ,
'error_message' => 'InvalidReturnType' ,
],
2018-01-09 22:46:55 -05:00
'alwaysCheckReturnType' => [
' < ? php
class A {}
/**
* @ return A
* @ psalm - suppress MismatchingDocblockReturnType
*/
2018-01-11 15:50:45 -05:00
function foo () : B {
2020-03-22 10:44:48 -04:00
return new A ;
2018-01-09 22:46:55 -05:00
} ' ,
'error_message' => 'UndefinedClass' ,
],
2018-01-10 00:07:47 -05:00
'preventBadBoolean' => [
' < ? php
2018-01-11 15:50:45 -05:00
function foo () : boolean {
2018-01-10 00:07:47 -05:00
return true ;
} ' ,
'error_message' => 'UndefinedClass' ,
],
2019-05-15 18:41:26 -04:00
'undefinedDocblockClassCall' => [
' < ? php
class B {
/**
* @ return A
* @ psalm - suppress UndefinedDocblockClass
* @ psalm - suppress InvalidReturnStatement
* @ psalm - suppress InvalidReturnType
*/
public function foo () {
return new stdClass ();
}
public function bar () {
$this -> foo () -> bar ();
}
}
' ,
'error_message' => 'UndefinedDocblockClass' ,
],
2020-08-30 11:44:14 -04:00
'preventBadTKeyedArrayFormat' => [
2018-01-19 16:06:30 -05:00
' < ? php
/**
* @ param array {} $arr
*/
function bar ( array $arr ) : void {} ' ,
'error_message' => 'InvalidDocblock' ,
2018-01-20 11:48:16 -05:00
],
2018-02-01 01:10:27 -05:00
'noPhpStormAnnotationsThankYou' => [
' < ? php
/** @param ArrayIterator|string[] $i */
function takesArrayIteratorOfString ( ArrayIterator $i ) : void {} ' ,
'error_message' => 'MismatchingDocblockParamType' ,
],
'noPhpStormAnnotationsPossiblyInvalid' => [
' < ? php
/** @param ArrayIterator|string[] $i */
function takesArrayIteratorOfString ( $i ) : void {
$s = $i -> offsetGet ( " a " );
} ' ,
'error_message' => 'PossiblyInvalidMethodCall' ,
],
2018-03-29 02:20:19 -04:00
'doubleBar' => [
' < ? php
/** @param PDO||Closure|numeric $a */
function foo ( $a ) : void {} ' ,
'error_message' => 'InvalidDocblock' ,
],
2018-04-05 14:11:57 -04:00
'badStringVar' => [
' < ? php
/** @var string; */
$a = " hello " ; ' ,
'error_message' => 'InvalidDocblock' ,
],
2018-04-15 18:16:31 -04:00
'badCallableVar' => [
' < ? php
/** @return Closure(int): */
function foo () : callable {
2018-09-17 12:15:45 -04:00
return function () : void {};
2018-04-15 18:16:31 -04:00
} ' ,
'error_message' => 'InvalidDocblock' ,
],
2018-04-24 21:27:31 -04:00
'hyphenInType' => [
' < ? php
/**
* @ return - Description
*/
function example () {
return " placeholder " ;
} ' ,
'error_message' => 'InvalidDocblock' ,
],
2018-06-11 17:23:28 -04:00
'badAmpersand' => [
' < ? php
/** @return &array */
function foo () : array {
return [];
} ' ,
'error_message' => 'InvalidDocblock' ,
],
2018-07-15 17:23:17 -04:00
'invalidTypeAlias' => [
' < ? php
/**
* @ psalm - type CoolType = A | B >
*/
class A {} ' ,
'error_message' => 'InvalidDocblock' ,
],
2020-08-30 11:44:14 -04:00
'typeAliasInTKeyedArray' => [
2018-08-08 22:44:02 -04:00
' < ? php
/**
* @ psalm - type aType null | " a " | " b " | " c " | " d "
*/
/** @psalm-return array{0:bool,1:aType} */
function f () : array {
return [( bool ) rand ( 0 , 1 ), rand ( 0 , 1 ) ? " z " : null ];
} ' ,
'error_message' => 'InvalidReturnStatement' ,
],
2019-01-08 14:50:45 -05:00
'noCrashOnHalfDoneArrayPropertyType' => [
' < ? php
class A {
/** @var array< */
private $foo = [];
} ' ,
'error_message' => 'InvalidDocblock' ,
],
2020-08-30 11:44:14 -04:00
'noCrashOnHalfDoneTKeyedArrayPropertyType' => [
2019-01-08 14:50:45 -05:00
' < ? php
class A {
/** @var array{ */
private $foo = [];
} ' ,
'error_message' => 'InvalidDocblock' ,
],
2019-01-08 15:11:57 -05:00
'noCrashOnInvalidClassTemplateAsType' => [
' < ? php
/**
* @ template T as ' . '
*/
class A {} ' ,
'error_message' => 'InvalidDocblock' ,
],
'noCrashOnInvalidFunctionTemplateAsType' => [
' < ? php
/**
* @ template T as ' . '
*/
function foo () : void {} ' ,
'error_message' => 'InvalidDocblock' ,
],
2019-01-09 11:51:29 -05:00
'returnTypeNewLineIsIgnored' => [
' < ? php
/**
* @ return
* Some text
*/
function foo () {} ' ,
'error_message' => 'MissingReturnType' ,
],
2019-01-18 00:56:24 -05:00
'objectWithPropertiesAnnotationNoMatchingProperty' => [
' < ? php
/** @param object{foo:string} $o */
function foo ( object $o ) : string {
return $o -> foo ;
}
class A {}
foo ( new A ); ' ,
'error_message' => 'InvalidArgument' ,
],
2019-01-26 17:30:44 -05:00
'badVar' => [
' < ? php
/** @var Foo */
$a = $_GET [ " foo " ]; ' ,
2019-05-15 18:41:26 -04:00
'error_message' => 'UndefinedDocblockClass' ,
2019-01-26 17:30:44 -05:00
],
2019-02-07 18:10:32 -05:00
'badPsalmType' => [
' < ? php
/**
* @ psalm - type Foo = array { a : }
*/ ' ,
'error_message' => 'InvalidDocblock' ,
],
2019-02-23 11:02:04 -05:00
'mismatchingDocblockParamName' => [
' < ? php
2019-02-27 16:00:44 -05:00
/** @param string[] $bar */
function f ( array $barb ) : void {} ' ,
2019-02-28 19:46:40 +00:00
'error_message' => 'InvalidDocblockParamName - src' . DIRECTORY_SEPARATOR . 'somefile.php:2:41' ,
2019-02-23 11:02:04 -05:00
],
2019-02-27 09:08:27 -05:00
'nonEmptyArrayCalledWithEmpty' => [
' < ? php
/** @param non-empty-array<string> $arr */
function foo ( array $arr ) : void {
foreach ( $arr as $a ) {}
echo $a ;
}
foo ([]); ' ,
'error_message' => 'InvalidArgument' ,
],
2019-03-02 08:35:50 -05:00
'nonEmptyArrayCalledWithEmptyInNamespace' => [
' < ? php
namespace ns ;
/** @param non-empty-array<string> $arr */
function foo ( array $arr ) : void {
foreach ( $arr as $a ) {}
echo $a ;
}
foo ([]); ' ,
'error_message' => 'InvalidArgument' ,
],
2019-02-27 09:08:27 -05:00
'nonEmptyArrayCalledWithArray' => [
' < ? php
/** @param non-empty-array<string> $arr */
function foo ( array $arr ) : void {
foreach ( $arr as $a ) {}
echo $a ;
}
/** @param array<string> $arr */
function bar ( array $arr ) {
foo ( $arr );
} ' ,
2019-04-25 18:02:19 -04:00
'error_message' => 'ArgumentTypeCoercion' ,
2019-02-27 09:08:27 -05:00
],
2019-04-09 13:58:49 -04:00
'spreadOperatorArrayAnnotationBadArg' => [
' < ? php
/** @param string[] $s */
function foo ( string ... $s ) : void {}
foo ( 5 ); ' ,
'error_message' => 'InvalidScalarArgument' ,
],
'spreadOperatorArrayAnnotationBadSpreadArg' => [
' < ? php
/** @param string[] $s */
function foo ( string ... $s ) : void {}
foo ( ... [ 5 ]); ' ,
'error_message' => 'InvalidScalarArgument' ,
],
2019-04-09 14:29:09 -04:00
'spreadOperatorByRefAnnotationBadCall1' => [
' < ? php
/** @param string &...$s */
function foo ( &... $s ) : void {}
$a = 1 ;
foo ( $a ); ' ,
'error_message' => 'InvalidScalarArgument' ,
],
'spreadOperatorByRefAnnotationBadCall2' => [
' < ? php
/** @param string ...&$s */
function foo ( &... $s ) : void {}
$b = 2 ;
foo ( $b ); ' ,
'error_message' => 'InvalidScalarArgument' ,
],
'spreadOperatorByRefAnnotationBadCall3' => [
' < ? php
/** @param string[] &$s */
function foo ( &... $s ) : void {}
$c = 3 ;
foo ( $c ); ' ,
'error_message' => 'InvalidScalarArgument' ,
],
2019-06-01 16:57:33 -04:00
'identifyReturnType' => [
' < ? php
/** @return array{hello: string} */
function foo () {} ' ,
'error_message' => 'InvalidReturnType - src' . DIRECTORY_SEPARATOR . 'somefile.php:2:33' ,
],
2019-06-03 15:46:25 -04:00
'invalidParamDocblockAsterisk' => [
' < ? php
/**
* @ param * $reference
*/
function f ( $reference ) {} ' ,
2019-07-05 16:24:00 -04:00
'error_message' => 'MissingDocblockType' ,
2019-06-26 15:11:16 -04:00
],
2020-03-09 15:35:02 +00:00
'canNeverReturnDeclaredType' => [
' < ? php
/** @psalm-return false */
function alwaysFalse () : bool
{
return true ;
} ' ,
'error_message' => 'InvalidReturnStatement - src' . DIRECTORY_SEPARATOR . 'somefile.php:6:32' ,
],
'falsableWithExpectedTypeTrue' => [
' < ? php
/** @psalm-return true */
function alwaysFalse ()
{
return false ;
} ' ,
'error_message' => 'FalsableReturnStatement - src' . DIRECTORY_SEPARATOR . 'somefile.php:6:32' ,
],
2020-05-30 22:54:16 +02:00
'DuplicatedParam' => [
' < ? php
/**
* @ psalm - param array $arr
* @ psalm - param array $arr
*/
function bar ( array $arr ) : void {} ' ,
'error_message' => 'InvalidDocblock - src' . DIRECTORY_SEPARATOR . 'somefile.php:6:21 - Found duplicated @param or prefixed @param tag in docblock for bar' ,
],
'DuplicatedReturn' => [
' < ? php
/**
* @ return void
* @ return void
*/
function bar ( array $arr ) : void {} ' ,
'error_message' => 'InvalidDocblock - src' . DIRECTORY_SEPARATOR . 'somefile.php:6:21 - Found duplicated @return or prefixed @return tag in docblock for bar' ,
],
2020-08-30 11:44:14 -04:00
'missingClassForTKeyedArray' => [
2020-07-24 09:38:51 -04:00
' < ? php
interface I {
/** @return object{id: int, a: int} */
public function run ();
}
class C implements I {
/** @return X */
public function run () {}
} ' ,
'error_message' => 'ImplementedReturnTypeMismatch'
],
2017-04-24 23:45:02 -04:00
];
2016-12-31 00:14:00 -05:00
}
2016-12-11 23:41:11 -05:00
}